English Русский
preview
Einführung in MQL5 (Teil 38): Beherrschung von API und WebRequest in MQL5 (XII)

Einführung in MQL5 (Teil 38): Beherrschung von API und WebRequest in MQL5 (XII)

MetaTrader 5Integration |
20 0
ALGOYIN LTD
Israel Pelumi Abioye

Einführung

Willkommen zurück zu Teil 38 der Serie „Einführung in MQL5“. Im vorigen Artikel haben wir uns auf die Schaffung eines soliden Rahmens für die Nutzung von MQL5 zur Kommunikation mit externen Systemen konzentriert. Wir haben uns die Verwendung von API-Endpunkten, die Funktionsweise der WebRequest-Funktion, die Vorbereitung von Authentifizierungskomponenten wie Zeitstempel und Signaturen sowie den Empfang und die Interpretation von Serverantworten angesehen. Bei der Arbeit mit privaten oder vertraulichen API-Interaktionen sind diese Konzepte unverzichtbar. 

Wie in den vorangegangenen Artikeln dieser Reihe verfolgen wir auch in diesem Artikel einen vollständig projektorientierten Ansatz. Anstatt isoliert über die Theorie zu sprechen, werden wir jede Idee in einem realen Prozess in die Praxis umsetzen. Dieser Ansatz hilft Ihnen, sowohl die Funktionsweise der einzelnen Teile als auch deren Zusammenspiel in einer konkreten Integrationssituation zu verstehen. Dieser Artikel schließt die API-Reihe ab und vollendet den bisherigen Lernweg.

In diesem Artikel werden wir einen MQL5 Expert Advisor erstellen, der Marktdaten durch die Interaktion mit einer externen Plattform wie Binance abruft und analysiert. Das Projekt beginnt mit der Auswahl eines bestimmten Coins auf der Website und der Verwendung der API zum Abrufen von Kerzendaten. Um in der letzten geschlossenen Kerze das Muster „Bullish Engulfing“ zu finden, werden die letzten drei geschlossenen 30-Minuten-Kerzen herangezogen. Wir werden den Mechanismus zur programmatischen Identifizierung dieses Musters erläutern, damit wir MQL5 verwenden können, um es genau zu erkennen. Wir werden auch erörtern, wie die Orders auf der Grundlage dieser Bedingungen über die API tatsächlich gesendet werden.

Wir werden uns darauf konzentrieren, wie Marktdaten abgerufen und analysiert werden, wie MetaTrader 5 sicher Anfragen erstellt und wie Handelsmuster während dieses Prozesses programmatisch bewertet werden. Dieses Projekt zeigt, wie MQL5 als Brücke zwischen MetaTrader 5 und Plattformen außerhalb des MetaTrader-Ökosystems fungieren kann und über die herkömmliche chartbasierte Logik hinausgeht. Am Ende dieses Artikels verfügen wir über einen klaren und wiederverwendbaren Arbeitsablauf zum Abrufen von Kerzendaten, zum Verstehen von Kerzenmustern, wie z. B. Bullish Engulfing, und zur Vorbereitung Ihres Expert Advisors, um auf Marktbedingungen zu reagieren. Sie werden auch ein gutes Verständnis dafür entwickeln, wie dieser Ansatz für andere Muster, Zeitrahmen und fortgeschrittene Automatisierungsszenarien angepasst werden kann.

Anmerkung:

Dieser Artikel ist ausschließlich zu Bildungszwecken geschrieben. Alle Beispiele, Projekte und Demonstrationen sollen erklären, wie MQL5 in realen Integrationsszenarien funktioniert. Es handelt sich nicht um eine Empfehlung oder Aufforderung zur Ausführung von Live-Aufträgen oder zur Durchführung von Finanzaktionen. Der Schwerpunkt liegt ganz auf dem Lernen, Experimentieren und Verstehen, wie man mit APIs und der WebRequest-Funktion in MQL5 in einem kontrollierten und pädagogischen Kontext arbeitet.


Abfrage und Handhabung von Kerzendaten

Die Abfrage der Details der drei letzten Kerzen ist der erste Schritt zur Erstellung dieses Expert Advisors. Normalerweise wäre dies ganz einfach, da das Symbol bereits auf dem MetaTrader 5-Chart vorhanden ist. Da die Daten für dieses Projekt jedoch direkt von Binance stammen, gibt es den Vermögenswert, den wir bewerten, nicht auf dem MetaTrader 5. Daher können wir uns nicht auf die in MetaTrader 5 eingebauten Preisreihen oder Indikatorfunktionen verlassen. Wir beginnen damit, einen API-Aufruf an Binance zu senden, um Kerzendaten zu erhalten, um dieses Problem zu lösen. Diese Strategie ist vergleichbar mit der in Teil 29 dieser Serie gezeigten Strategie, bei der wir die Kerzendaten direkt von einer Server-Antwort übernommen haben, anstatt die Chartdaten von MetaTrader 5 zu verwenden. Das Konzept ist hier ähnlich: Wir fragen Binance nach den Rohdaten der Kerzen, die wir dann manuell in unserem Expert Advisor verarbeiten.

Das Senden einer API-Anfrage, um aktuelle Kerzendaten für die ausgewählte Coin auf Binance zu erhalten, ist das erste, was der EA tut. Das Verständnis der JSON-Struktur, die Binance verwendet, um Kerzendaten anzuzeigen, ist der nächste Schritt nach dem Erhalt der Antwort. Kerzen werden von Binance als strukturierte Arrays zurückgegeben, mit zahlreichen Werten in jeder Kerze. Sobald wir das JSON-Muster verstanden haben, verwenden wir verschiedene MQL5-String-Verarbeitungsroutinen, um die wichtigsten Fakten zu extrahieren. Der Eröffnungskurs, der Höchstkurs, der Tiefstkurs, der Schlusskurs und die Eröffnungszeit jeder Kerze sind Beispiele für ähnliche Werte, die mit diesen Funktionen sortiert und zusammengefasst werden können. Wir können schnell feststellen, ob die OHLC-Daten bullisch sind, indem wir die letzte geschlossene Kerze identifizieren.

Es ist wichtig zu erkennen, dass wir nicht unendlich viele API-Abfragen senden sollten, nur weil wir es können. Zu häufiges Senden von Anfragen kann zu vorübergehenden Einschränkungen oder verweigerten Antworten führen, da Binance strenge Limits für seine API festgelegt hat. Aus diesem Grund ist der Zeitpunkt einer Anfrage ebenso wichtig wie ihre Genauigkeit. Die effektivste Methode besteht darin, die API-Anfrage genau zum Zeitpunkt der Eröffnung jeder neuen 30-Minuten-Kerze zu senden, da dieser Expert Advisor mit geschlossenen 30-Minuten-Kerzen arbeitet. Zu diesem Zeitpunkt ist die vorherige Kerze vollständig geschlossen, und ihre Informationen sind korrekt und endgültig. Würde die Anfrage zu einem anderen Zeitpunkt erfolgen, würde sie entweder unvollständige Kerzendaten liefern oder API-Aufrufe verschwenden, die keine neuen Informationen liefern.

Wir minimieren unnötigen Netzwerkverkehr, halten uns an die Anfragelimits von Binance und garantieren, dass die von uns untersuchten Daten durchgängig eine vollständig geschlossene Kerze darstellen, indem wir unsere API-Anfragen mit den 30-minütigen Kerzenöffnungszeiten koordinieren. Da der Expert Advisor seine Analyse nur dann durchführt, wenn neue Informationen verfügbar sind, bleiben auch bei dieser Methode die Effizienz und Vorhersagbarkeit des Expert Advisors erhalten.

Beispiel:

datetime last_bar_time = 0;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

   datetime current_m30_time = iTime(_Symbol,PERIOD_M30,0);

   if(current_m30_time != last_bar_time)
     {

      //SEND REQUEST

      last_bar_time =  current_m30_time;
     }
  }

Erläuterung:

Um die Eröffnungszeit der letzten 30-Minuten-Kerze zu ermitteln, mit der er gearbeitet hat, deklariert der Expert Advisor zunächst eine Variable. Der EA kann die erste gültige Kerze leicht identifizieren und feststellen, ob ein neuer API-Aufruf erforderlich ist, da dieser Wert auf Null initialisiert wird. Jedes Mal, wenn ein neuer Markttick für das gewählte Symbol empfangen wird, wird die Hauptfunktion des EA ausgelöst. Jeder Tick bedeutet eine winzige Preisveränderung, und die Funktion bewertet den Markt kontinuierlich. Bei dieser Methode ermittelt der EA die Eröffnungszeit der letzten 30-Minuten-Kerze. Dies bietet einen konkreten Anhaltspunkt für den Beginn der aktuellen Kerze.

Der EA vergleicht dann die gespeicherte letzte Barzeit mit der Eröffnungszeit der aktuellen Kerze. Eine neue Kerze hat seit der letzten Überprüfung begonnen, wenn die beiden Zeiten voneinander abweichen. Als Reaktion auf diese Situation wird eine API-Anfrage zum Abrufen von Kerzendaten gesendet. Der EA vermeidet sinnlose Aufrufe und garantiert, dass er unter API-Beschränkungen effektiv läuft, indem er nur Anfragen sendet, wenn eine neue Kerze geöffnet wird. Der EA ändert die Variable „last bar time“, um die Öffnungszeit der aktuellen Kerze nach dem Senden der API-Anfrage wiederzugeben. Dadurch wird sichergestellt, dass der EA erst dann eine neue Anfrage stellt, wenn die nächste 30-Minuten-Kerze beginnt, und dieselbe Kerze nicht mehr als einmal verarbeitet wird. Diese Methode bewahrt die Effizienz und Genauigkeit der Datenabfrage.

Analogie:

Gehen wir davon aus, dass wir alltägliche Ereignisse in einem Notizblock festhalten. Um zu vermeiden, dass wir zweimal am Tag denselben Eintrag vornehmen, erfassen wir die Informationen nur einmal. Um festzustellen, ob es sich bei dem aktuellen Tag um einen neuen Tag oder um den gleichen Tag handelt, den wir zuvor dokumentiert haben, notieren wir auch das letzte Datum, das wir erfasst haben. Jeder Markttick ist in diesem Fall so, als würde man im Laufe des Tages immer wieder auf einen Kalender schauen. Wir würden nur dann eine neue Notiz machen, wenn sich das Datum wirklich ändert, nicht jedes Mal, wenn wir nachschauen. Dieses Datum wird durch die Eröffnungszeit der aktuellen 30-Minuten-Kerze dargestellt, die anzeigt, wann Handlungsbedarf besteht.

Ähnlich wie beim Senden einer API-Anfrage, um neue Kerzendaten zu erhalten, zeichnen wir die Details auf, wenn ein neuer Tag beginnt oder, für den EA, wenn eine neue Kerze geöffnet wird. Um zu verhindern, dass derselbe Tag erneut registriert wird, ändern wir das zuletzt notierte Datum, sobald es erfasst wurde. So wie der EA die API-Abfragen auf eine pro Kerze beschränkt, garantiert dies, dass jeder Tag nur einmal bearbeitet wird und die Aufzeichnungen sauber bleiben.

Beispiel:
datetime last_bar_time = 0;
string method_bar = "GET";
string url_bar = "https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=30m&limit=3";
string headers_bar = "";
int time_out_bar = 5000;
char   data_bar[];
char   result_bar[];
string result_headers_bar;
string server_result;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

   datetime current_m30_time = iTime(_Symbol,PERIOD_M30,0);

   if(current_m30_time != last_bar_time)
     {

      WebRequest(method_bar, url_bar, headers_bar, time_out_bar, data_bar, result_bar, result_headers_bar);
      server_result = CharArrayToString(result_bar);

      Print(server_result);
      last_bar_time =  current_m30_time;
     }
  }

Ausgabe:

Erläuterung:

Die für den Aufruf der Binance-API erforderlichen Variablen werden im ersten Abschnitt vorgestellt. Durch die Angabe der Methode „GET“ wird deutlich gemacht, dass die Anfrage dazu dient, Daten vom Server zu lesen, und nicht dazu, Aktualisierungen zu übermitteln oder Änderungen vorzunehmen. Die URL-Variable enthält die Adresse des Binance-Endpunkts, der Kerzendaten bereitstellt. Während die Adresse selbst den Server identifiziert, gibt der Endpunkt die genaue Ressource auf dem Server an, in diesem Beispiel den Endpunkt für Kerzeninformationen. Der Server erhält zusätzliche Anweisungen im URL-Abfrage-String. Das Limit weist den Server beispielsweise an, wie viele Kerzen zurückgegeben werden sollen, das Intervall bestimmt die Länge jeder Kerze, und der Symbolparameter identifiziert das Handelspaar, für das die Daten benötigt werden.

Da die Anfrage keine Authentifizierungs- oder andere Metadaten enthält, bleiben die Header leer. Wenn keine Antwort empfangen wird, bestimmt der Timeout-Wert, wie lange das Programm wartet, bevor es die Anfrage abbricht. Die Anfragedaten, der Antwort-Header und die Rohdaten des Servers werden alle in Arrays erfasst. Um die Analyse und das Parsing zu erleichtern, wird die transformierte Serverantwort in einer zusätzlichen String-Variablen gespeichert. Die WebRequest-Funktion wird dann mit den erforderlichen Parametern aufgerufen. Dies verwendet die angegebene URL, Header und Timeout, um eine GET-Anfrage an den Binance-Server zu senden. Die Funktion liefert Details über den Anfragestatus und speichert die Rohantwort des Servers im Antwort-Array. Die Byte-Daten werden dann zum leichteren Lesen in eine Zeichenkette umgewandelt. Wenn diese Zeichenfolge angezeigt wird, wird die vollständige JSON-Antwort mit den Kerzendaten angezeigt.

Analogie:

Stellen wir uns vor, wir rufen in einer Bibliothek an und fragen nach den Daten der letzten drei Tage. Die Serveradresse wird durch das Bibliotheksgebäude selbst repräsentiert, während die historischen Daten von der jeweiligen Abteilung bearbeitet werden, die wir anfordern, ähnlich wie der API-Pfad. Bei der Anfrage geben wir die Stadt, die gewünschte Anzahl von Tagen und den Umfang der Informationen in den Unterlagen an. Diese Richtlinien sind ähnlich wie die der URL hinzugefügten Parameter Symbol, Intervall und Limit. Danach schicken wir das Schreiben ab und warten auf eine Antwort. Wenn er zurückkommt, steht ein Fach bereit, um die Antwort zu empfangen. Die Antwort wird vom Büro als eine Reihe von Symbolen gesendet, die codiert sind und nicht sofort gelesen werden können. Um das Wetter der letzten drei Tage zu verstehen, entschlüsseln wir die Nachricht in ein lesbares Format und lesen sie dann.

In ähnlicher Weise verwendet der EA die voreingestellte URL und die Abfrageparameter, um eine GET-Request an Binance zu übermitteln, pausiert, während der Request verarbeitet wird, empfängt die Kerzendaten als Rohbytes, wandelt sie in ein lesbares Textformat um und druckt sie zur Überprüfung aus. Der EA kann auf diese Weise die neuesten Kerzeninformationen für das ausgewählte Handelspaar sammeln.


Extrahieren von OHLC- und Zeitinformationen aus Binance-Kerzendaten

In diesem Abschnitt geht es darum, die relevanten Daten aus der Serverantwort von Binance zu extrahieren. JSON-Datenstrukturen fallen je nach Plattform oder Dienst unterschiedlich aus. Dies ist eine wichtige Lektion in Sachen API-Integration. Aufgrund dieser Unterschiede können die Kerzendaten in den verschiedenen Diensten unterschiedlich angeordnet sein. Das genaue Format der zurückgegebenen Daten zu verstehen, ist der erste Schritt im Umgang mit jeder Serverantwort, wie bereits in Teil 29 dieser Serie beschrieben. Jede Kerze in den Kerzendaten von Binance wird als strukturiertes JSON-Array mit mehreren Werten gesendet, darunter Eröffnungszeit, Eröffnungskurs, Höchstkurs, Tiefstkurs, Schlusskurs, Volumen und andere Metadaten. Die Stellen in der Antwort, die den OHLC-Werten und der Kerzeneröffnungszeit entsprechen, müssen eindeutig identifiziert werden, bevor wir Handelsentscheidungen treffen können.

Nach dem Verstehen der Struktur folgt die Reinigung der Antwort. Dies bedeutet, dass überflüssige Zeichen, die im JSON-Format vorhanden, aber für Berechnungen nicht hilfreich sind, wie Klammern, Kommas und Anführungszeichen, entfernt werden. Sobald die Rohdaten bereinigt sind, können wir damit beginnen, ähnliche Daten zu kombinieren. Um beispielsweise die Eröffnungs-, Höchst-, Tiefst- und Schlusswerte für eine einzelne Kerze als eine logische Einheit zu behandeln, müssen sie gruppiert werden. Wir machen die Daten in MQL5 viel handlicher, indem wir die extrahierten Werte in Arrays anordnen. Wir können Vergleiche anstellen, bullische oder bärische Kerzen identifizieren und eine Logik anwenden, die von der letzten geschlossenen Kerze abhängt, da jedes Feld eine bestimmte Art von Information darstellen kann, wie z. B. Eröffnungszeiten oder OHLC-Werte.

Binance liefert Kerzendaten in einem JSON-Array-Format. Die Antwort ist ein Array mit mehreren inneren Arrays auf der obersten Ebene. Jedes innere Array stellt eine einzelne Kerze dar. In ihrer einfachsten Form sieht die Struktur wie folgt aus:

[
[array 1],
[array 2],
[array 3]
]

Alle Kerzendaten werden in jedem inneren Array für eine einzelne Kerze gespeichert. Aufgrund der festen Anordnung dieser Werte wird an jeder Stelle des inneren Arrays stets die gleiche Art von Daten übermittelt.

Beispiel:

int array_count;
string candle_data[];
string first_bar_data;
string first_bar_data_array[];
string second_bar_data;
string second_bar_data_array[];
string third_bar_data;
string third_bar_data_array[];
if(current_m30_time != last_bar_time)
  {

   WebRequest(method_bar, url_bar, headers_bar, time_out_bar, data_bar, result_bar, result_headers_bar);
   server_result = CharArrayToString(result_bar);

// Print(server_result);
   array_count = StringSplit(server_result,']', candle_data);

   first_bar_data = candle_data[0];
   StringReplace(first_bar_data,"[[","");
   StringReplace(first_bar_data,"\"","");
   StringSplit(first_bar_data,',',first_bar_data_array);

   second_bar_data = candle_data[1];
   StringReplace(second_bar_data,",[","");
   StringReplace(second_bar_data,"\"","");
   StringSplit(second_bar_data,',',second_bar_data_array);

   third_bar_data = candle_data[2];
   StringReplace(third_bar_data,",[","");
   StringReplace(third_bar_data,"\"","");
   StringSplit(third_bar_data,',',third_bar_data_array);

   last_bar_time =  current_m30_time;
  }

Erläuterung:

Die Antwort des Servers wird mithilfe von Variablen verarbeitet. Die Werte der ersten drei Kerzen werden in verschiedenen Arrays gespeichert, die Segmente werden gezählt, und die Kerzenblöcke werden in einem String-Array gespeichert. Ein Element pro Kerze wird durch Aufteilung der Antwort mithilfe der schließenden Klammer unter Beibehaltung einer gewissen JSON-Syntax erzeugt. Um die erste Kerze zu verarbeiten, wird das erste Element des Kerzendaten-Arrays extrahiert. Öffnende Klammern und Anführungszeichen sind Beispiele für überflüssige Zeichen, die aus diesem Text entfernt wurden, um die Daten zu bereinigen. Die bereinigte Zeichenkette wird dann in ein Array unterteilt, wobei jeder Index einen bestimmten Kerzenwert durch Kommas bezeichnet. Um sicherzustellen, dass die zweite Kerze die gleiche Struktur wie die erste Kerze hat und vollständig getrennt ist, durchläuft sie den gleichen Prozess: Ihr Array-Element wird von unnötigen Zeichen befreit und durch Kommas getrennt.

Bei der dritten Kerze wird genauso verfahren. Unerwünschte Zeichen werden eliminiert, der eigentliche Text wird wiederhergestellt und die bereinigte Zeichenfolge wird in verschiedene Werte unterteilt. Jede Kerze hat ihr eigenes Array mit geordneten Werten, die die Daten der Kerze nach dieser Serie wiedergeben.

Analogie:

Stellen wir uns die Antwort des Servers wie eine lange Papierrolle vor, auf der mehrere Quittungen nacheinander ausgegeben werden. Jede Quittung stellt eine Kerze dar, aber anfangs klebt alles auf derselben Rolle, sodass es schwer zu sehen oder zu benutzen ist. Der erste Schritt ist das Zerschneiden der Rolle in einzelne Belege. Dies ist der Fall, wenn die Reaktion in mehrere Teile aufgeteilt wird. Auch wenn jeder Artikel jetzt eine einzelne Kerze darstellt, sind immer noch unnötige Beschriftungen, Umrandungen und Symbole darauf angebracht.

Die ursprüngliche Quittung wird dann von jeglicher äußerer Verpackung, wie z. B. attraktiven Umrandungen oder Anführungszeichen, befreit, sodass nur der eigentliche Inhalt übrig bleibt. Nach der Reinigung lesen wir den Beleg Zeile für Zeile und notieren jede Information in einer eigenen Spalte. Dies verdeutlicht und vereinfacht den Inhalt. Für die zweite Quittung gehen wir dann genauso vor. Sie isolieren es von den anderen, entfernen die unnötige Formatierung und ordnen seinen Inhalt in verschiedene Felder ein. Um zu gewährleisten, dass jeder Beleg auf die gleiche Weise bearbeitet wird, folgt der dritte Beleg dem gleichen Verfahren.

Was als lange, unübersichtliche Papierrolle begann, hat sich am Ende dieses Prozesses in mehrere gut organisierte Dokumente verwandelt. Jeder Datensatz enthält bestimmte Details, die nun verglichen, untersucht und zur Entscheidungsfindung herangezogen werden können. Auf diese Weise werden aus der rohen Serverantwort strukturierte Kerzendaten erzeugt, die der Expert Advisor zuverlässig nutzen kann. Die abgerufenen Werte müssen dann in verschiedenen Variablen gespeichert werden. Jetzt ist es einfach, jeden Wert einer bestimmten Variablen zuzuordnen, da die Kerzendaten bereits bereinigt und in Arrays organisiert wurden. Während die Eröffnungs-, Höchst-, Tiefst- und Schlusswerte unabhängig voneinander gespeichert werden, kann die Eröffnungszeit in einer eigenen Variablen erfasst werden.

Beispiel:

long bar1_time_s;
datetime bar1_time;
long bar2_time_s;
datetime bar2_time;
long bar3_time_s;
datetime bar3_time;

double bar1_open;
double bar2_open;
double bar3_open;

double bar1_high;
double bar2_high;
double bar3_high;

double bar1_low;
double bar2_low;
double bar3_low;

double bar1_close;
double bar2_close;
double bar3_close;
if(current_m30_time != last_bar_time)
  {

   WebRequest(method_bar, url_bar, headers_bar, time_out_bar, data_bar, result_bar, result_headers_bar);
   server_result = CharArrayToString(result_bar);

// Print(server_result);

   array_count = StringSplit(server_result,']', candle_data);

   first_bar_data = candle_data[0];
   StringReplace(first_bar_data,"[[","");
   StringReplace(first_bar_data,"\"","");
   StringSplit(first_bar_data,',',first_bar_data_array);

   second_bar_data = candle_data[1];
   StringReplace(second_bar_data,",[","");
   StringReplace(second_bar_data,"\"","");
   StringSplit(second_bar_data,',',second_bar_data_array);

   third_bar_data = candle_data[2];
   StringReplace(third_bar_data,",[","");
   StringReplace(third_bar_data,"\"","");
   StringSplit(third_bar_data,',',third_bar_data_array);

   bar1_time_s = (long)StringToInteger(first_bar_data_array[0])/1000;
   bar1_time = (datetime)bar1_time_s;
   bar2_time_s = (long)StringToInteger(second_bar_data_array[0])/1000;
   bar2_time = (datetime)bar2_time_s;
   bar3_time_s = (long)StringToInteger(third_bar_data_array[0])/1000;
   bar3_time = (datetime)bar3_time_s;

   bar1_open = StringToDouble(first_bar_data_array[1]);
   bar2_open = StringToDouble(second_bar_data_array[1]);
   bar3_open = StringToDouble(third_bar_data_array[1]);

   bar1_high = StringToDouble(first_bar_data_array[2]);
   bar2_high = StringToDouble(second_bar_data_array[2]);
   bar3_high = StringToDouble(third_bar_data_array[2]);

   bar1_low = StringToDouble(first_bar_data_array[3]);
   bar2_low = StringToDouble(second_bar_data_array[3]);
   bar3_low = StringToDouble(third_bar_data_array[3]);

   bar1_close = StringToDouble(first_bar_data_array[4]);
   bar2_close = StringToDouble(second_bar_data_array[4]);
   bar3_close = StringToDouble(third_bar_data_array[4]);

   last_bar_time =  current_m30_time;
  }

Erläuterung:

Die abgerufenen Kerzendaten werden schließlich genau spezifizierten Variablen zugewiesen, die in diesem Abschnitt des Codes Zeit- und Preisinformationen angeben. Der Eröffnungszeitpunkt und die OHLC-Werte für jede Kerze werden vor der Zuordnung in einer Sammlung von Variablen angegeben. Für jede Kerze werden zwei Variablen in Bezug auf die Zeit angegeben. Die eine ist eine Datetime-Variable, die die umgewandelte Zeit in einem für den MetaTrader verständlichen Format speichert, während die andere eine lange Ganzzahl ist, die zur vorübergehenden Speicherung des rohen Zeitstempelwerts verwendet wird. Für den Wert jeder Kerze in den Preisdaten werden getrennte Double-Variablen deklariert. Durch die vorherige Deklaration dieser Variablen wird sichergestellt, dass jede Information einen eindeutigen und bedeutsamen Platz im Programm hat, und die Datenstruktur wird klarer.

Die Daten jeder Kerze werden in der von Binance festgelegten Reihenfolge in verschiedenen Arrays angeordnet. Das erste Element zeigt die Öffnungszeit der Kerze bei Index Null an. Am Anfang war es eine Zeichenfolge in Millisekunden. Die Zeichenfolge wird in eine ganze Zahl umgewandelt und durch 1.000 geteilt, um sie in MQL5 zu verarbeiten und Millisekunden in Sekunden umzuwandeln. Dadurch wird der Zeitstempel in Sekunden umgewandelt und die Millisekundengenauigkeit eliminiert. Der Wert kann nach dieser Umwandlung sicher in eine Datetime-Variable umgewandelt werden. Um sicherzustellen, dass alle Eröffnungszeiten genau mit dem Zeitsystem des MetaTrader übereinstimmen, wird dieser Vorgang für jede Kerze durchgeführt.

Der Eröffnungskurs der Kerze wird durch das folgende Element im Kerzen-Array an der Indexposition eins dargestellt. Dieser Wert wird in die entsprechende Variable für den Eröffnungspreis gestellt, nachdem er von Text in einen Doppelwert umgewandelt wurde. Der Höchstkurs der Kerze wird durch den Wert an der zweiten Indexposition dargestellt, der auf die gleiche Weise transformiert wird. Der Tiefstkurs befindet sich an Indexposition drei, der Schlusskurs an Indexposition vier. Um in numerischen Berechnungen und Vergleichen verwendet werden zu können, wird jeder dieser Werte separat von einer Zeichenkette in einen Double-Wert umgewandelt.

Jedes Kerzenfeld folgt dem gleichen Indizierungsmuster. Die Eröffnungszeit wird stets durch den Index Null dargestellt, der Eröffnungskurs durch den Index Eins, der Höchstkurs durch den Index Zwei, der Tiefstkurs durch den Index Drei und der Schlusskurs durch den Index Vier. Durch die Verwendung dieser festen Struktur kann der Expert Advisor jeden Wert genau lesen und zuordnen, ohne dass es zu Verwechslungen kommt. Die von der Binance-API erhaltenen Rohtextdaten wurden am Ende dieser Phase vollständig in korrekt getippte Variablen umgewandelt. Die Kurswerte werden nun als numerische Typen gespeichert, die analysiert werden können, während die Zeitwerte als Datetime-Objekte gespeichert werden, die sich leicht in den MetaTrader integrieren lassen. Dank dieser letzten Änderung kann der Expert Advisor die Kerzenrichtung genau einschätzen, z. B. ob die zuletzt geschlossene Kerze bullisch ist.

Analogie:

Stellen wir uns vor, der Server antwortet mit drei Umschlägen, die jeweils eine 30-minütige Kerzenübersicht enthalten. Jeder Envelope hat ein bestimmtes Design. In der ersten Zeile wird die Anfangszeit der Kerze angezeigt, in der zweiten der Eröffnungskurs, in der dritten der Höchstkurs, in der vierten der Tiefstkurs und in der fünften der Schlusskurs. Stellen wir uns vor, wir legen jeden Envelope in ein Regal, bevor wir dieses Wissen anwenden. Um den Zugriff zu erleichtern, muss jeder Envelope geleert und sein Inhalt ordentlich auf verschiedenen Fächern abgelegt werden. Die erste Zeile gibt die Zeit an, zu der die Kerze eröffnet wurde, ähnlich wie eine kleine Karte im Envelope. Sie stimmt nicht mit der Uhr in Ihrem Regal überein, weil sie in Millisekunden ausgedrückt wird. Bevor wir sie in den entsprechenden Steckplatz einlegen, konvertieren wir sie in ein Format, das Ihr System verstehen kann.

Die verbleibenden Zeilen im Envelope, die den Eröffnungs-, Höchst-, Tiefst- und Schlusswert anzeigen, werden dann in die entsprechenden Fächer eingeordnet. Sie stellen sicher, dass alles richtig angeordnet ist, indem wir für jeden der drei Umschläge das gleiche Verfahren anwenden. Da jedes Regal die gleiche Art von Daten aus allen drei Kerzen enthält, weist das Bücherregal schließlich eine klare und einheitliche Struktur auf, die eine einfache Analyse ermöglicht. Der nächste Schritt besteht darin, vergleichbare Daten in eigenen Arrays zu sammeln, nachdem die Details jeder Kerze sorgfältig geordnet worden sind. Legen wir alle offenen Karten aus den Regalen in eine Reihe, dann alle hohen Karten in eine andere Reihe, die niedrigen Karten in eine dritte Reihe und die letzten Karten in eine vierte. Durch diese Struktur ist jede Art von Daten leicht zu erhalten und zu verarbeiten.

Dies vereinfacht die Arbeit mit den Daten erheblich, da jedes Feld für jede Kerze die gleiche Art von Informationen enthält. Anstatt jede Kerze einzeln durchzugehen, können wir zum Beispiel einfach auf das entsprechende Feld zugreifen, wenn wir schnell den letzten Eröffnungskurs überprüfen oder die Höchststände der letzten drei Kerzen vergleichen möchten. Diese Anordnung erleichtert die Anwendung von Logik auf der Grundlage von Zeit, Preis oder anderen Kerzeneigenschaften und trägt zur Beschleunigung der Analyse bei.

Beispiel:

if(current_m30_time != last_bar_time)
  {

   WebRequest(method_bar, url_bar, headers_bar, time_out_bar, data_bar, result_bar, result_headers_bar);
   server_result = CharArrayToString(result_bar);

// Print(server_result);

   array_count = StringSplit(server_result,']', candle_data);

   first_bar_data = candle_data[0];
   StringReplace(first_bar_data,"[[","");
   StringReplace(first_bar_data,"\"","");
   StringSplit(first_bar_data,',',first_bar_data_array);

   second_bar_data = candle_data[1];
   StringReplace(second_bar_data,",[","");
   StringReplace(second_bar_data,"\"","");
   StringSplit(second_bar_data,',',second_bar_data_array);

   third_bar_data = candle_data[2];
   StringReplace(third_bar_data,",[","");
   StringReplace(third_bar_data,"\"","");
   StringSplit(third_bar_data,',',third_bar_data_array);

   bar1_time_s = (long)StringToInteger(first_bar_data_array[0])/1000;
   bar1_time = (datetime)bar1_time_s;
   bar2_time_s = (long)StringToInteger(second_bar_data_array[0])/1000;
   bar2_time = (datetime)bar2_time_s;
   bar3_time_s = (long)StringToInteger(third_bar_data_array[0])/1000;
   bar3_time = (datetime)bar3_time_s;

   bar1_open = StringToDouble(first_bar_data_array[1]);
   bar2_open = StringToDouble(second_bar_data_array[1]);
   bar3_open = StringToDouble(third_bar_data_array[1]);

   bar1_high = StringToDouble(first_bar_data_array[2]);
   bar2_high = StringToDouble(second_bar_data_array[2]);
   bar3_high = StringToDouble(third_bar_data_array[2]);

   bar1_low = StringToDouble(first_bar_data_array[3]);
   bar2_low = StringToDouble(second_bar_data_array[3]);
   bar3_low = StringToDouble(third_bar_data_array[3]);

   bar1_close = StringToDouble(first_bar_data_array[4]);
   bar2_close = StringToDouble(second_bar_data_array[4]);
   bar3_close = StringToDouble(third_bar_data_array[4]);

   datetime OpenTime[3] = {bar1_time, bar2_time, bar3_time};
   double   OpenPrice[3] = {bar1_open, bar2_open, bar3_open};
   double   ClosePrice[3] = {bar1_close, bar2_close, bar3_close};
   double   LowPrice[3] = {bar1_low, bar2_low, bar3_low};
   double   HighPrice[3] = {bar1_high, bar2_high, bar3_high};

   last_bar_time =  current_m30_time;
  }

Erläuterung:

Der EA fasst ähnliche Arten von Informationen zusammen, um die Daten aus den drei Kerzen zu organisieren. Ohne für jede Kerze unterschiedliche Werte verwalten zu müssen, ist es einfacher, auf die Eröffnungszeitpunkte der ältesten, mittleren und jüngsten Kerzen zuzugreifen, da sie in einer einzigen Gruppe zusammengefasst sind. Der EA kann den Anfangspreis jeder Kerze schnell vergleichen, indem er die gleiche Methode für die Startpreise verwendet. Neben dem Tief und dem Hoch jeder Kerze werden auch die Schlusskurse zusammengefasst. Berechnungen wie Trendanalysen, Hoch- und Tiefvergleiche oder das Erkennen von Auf- und Abwärtsmustern werden durch die Organisation dieser verknüpften Werte wesentlich erleichtert.

Im Wesentlichen ergibt diese Organisation eine strukturierte Tabelle, in der Zeit, Eröffnungs- und Schlusskurs, Tiefst- und Höchstkurs in Spalten und jede Kerze in einer Zeile dargestellt werden. Dies vereinfacht die Datenverwaltung und bildet die Grundlage für die automatisierte Handelslogik im EA oder die Kerzenmusteranalyse.

Analogie:

Nehmen wir an, wir haben drei Kästchen, die den drei vorherigen 30-Minuten-Kerzen auf Binance entsprechen. Die fünf Informationen, die in jedem Kästchen enthalten waren, waren der Zeitpunkt, zu dem die Kerze geöffnet wurde, der Eröffnungskurs, der Schlusskurs, der niedrigste und der höchste Kurs.

Sie haben sich dafür entschieden, alles nach Typ zu ordnen, anstatt drei verschiedene Kisten mit allen Informationen zu tragen. Die Öffnungszeiten der drei Kästchen werden zusammengefasst, ebenso die Öffnungs- und Schlusskurse sowie die niedrigsten und höchsten Preise. Sie müssen nicht mehr jedes Kästchen durchsehen, um den Eröffnungskurs der zweiten Kerze oder den höchsten Kurs der drei Kerzen zu ermitteln. Es ist schnell und einfach, auf die Daten zuzugreifen, sie zu vergleichen oder zu analysieren, da alles gut organisiert ist.

Beispiel:
if(ClosePrice[0] < OpenPrice[0] && ClosePrice[1] > OpenPrice[1] && ClosePrice[1] > HighPrice[0])
  {

   Print("BULLISH ENGULFING CONFIRMED");
//SEND A BUY ORDER TO BINANCE

  }
else
  {

// NO BULLSH ENGULFING
   Print("NO BULLISH ENGULFING CONFIRMED");

  }

Erläuterung:

Unter Verwendung der zuvor erstellten Kerzen-Arrays soll die Bedingung für ein „Bullish Engulfing“ identifiziert werden. Nur die beiden letzten geschlossenen Kerzen in dieser Anordnung sind für uns von Interesse. Insbesondere bezeichnet der Index 0 die dritte Kerze, die die älteste der drei Kerzen ist, der Index 1 die zweite Kerze, die die mittlere Kerze ist, und der Index 2 die aktuelle, sich noch bildende Kerze, die in dieser Musteridentifikation aufgrund ihrer Unvollständigkeit nicht verwendet wird.

Der erste Abschnitt der Bedingung bestimmt, ob die vorangegangene Kerze (Index 0, die dritte Kerze) bärisch ist. Dies geschieht durch die Gegenüberstellung des Eröffnungs- und des Schlusskurses der Kerze. Dies bedeutet, dass sich die Kerze während ihres Zeitraums nach unten bewegt hat, wenn der Schlusskurs niedriger ist als der Eröffnungskurs. Der zweite Abschnitt bestimmt, ob die letzte geschlossene Kerze (Index 1, die zweite Kerze) bullisch ist. Dies wird bestätigt, wenn der Schlusskurs der Kerze den Eröffnungskurs übersteigt, was auf einen Aufwärtstrend hindeutet.

Der letzte Abschnitt stellt sicher, dass die vorangegangene bärische Kerze vollständig von der aktuellen bullischen Kerze absorbiert wird. Dies wird bestätigt, indem man feststellt, ob der Schlussstand der bullischen Kerze höher ist als der Höchststand der bärischen Kerze. Wenn diese Bedingung erfüllt ist, bildet sich das Muster „Bullish Engulfing“, da der Körper der jüngsten Kerze den Körper der vorangegangenen Kerze vollständig umschließt. Der Code zeigt an, dass das Muster gefunden wurde, indem er „BULLISH ENGULFING CONFIRMED“ druckt, wenn alle diese Anforderungen erfüllt sind. In einem realen Handelsszenario könnte zu diesem Zeitpunkt ein Kaufauftrag an Binance erteilt werden.

Der andere Block wird ausgeführt, wenn eine der Anforderungen nicht erfüllt ist, und druckt „NO BULLISH ENGULFING CONFIRMED“, um zu zeigen, dass es kein legitimes bullisches Engulfing-Muster gibt. Unter Auslassung der sich noch bildenden aktuellen Kerze (Index 2), die nicht zur Bestätigung des Musters herangezogen werden kann, bietet diese Argumentation eine einfache, programmierte Methode zur Identifizierung einer bullischen Engulfing-Formation anhand der sortierten Kerzendaten.


Automatisieren von Aufträgen über die Binance API

Der nächste Schritt in diesem Kapitel ist die automatische Ausführung von Trades, nachdem wir nun wissen, wie wir ein bullisches Engulfing-Muster erkennen können. Hier werden wir uns auf die Verwendung der WebRequest-Funktion in MQL5 mit der API konzentrieren, um Aufträge an Binance zu senden. Im vorigen Artikel wurden nur der Zeitstempel und der geheime Schlüssel zur Erzeugung der Signatur verwendet. Dies war ausreichend, da nur der Zeitstempel und die generierte Signatur in den Abfrage-String aufgenommen wurden, um den Kontostand zu ermitteln. Doch dieses Mal sind für die Übermittlung einer Bestellung mehr Angaben als nur der Zeitstempel erforderlich.

Das Handelssymbol, die Auftragsseite, der Auftragstyp, der Betrag und der Zeitstempel sind nur einige der Elemente, die in den Abfrage-String aufgenommen werden, wenn Aufträge über die Binance-API erteilt werden. Es ist wichtig zu wissen, dass jeder im Abfrage-String verwendete Parameter bei der Erstellung der Signatur verwendet werden muss. Binance lehnt den Antrag ab, wenn irgendwelche Parameter während der Signaturerstellung fehlen.

Beispiel:

string url_t = "https://api.binance.com/api/v3/time";
string headers_t = "";
char result_t[];
string response_headers_t;
char data_t[];

string time;
string pattern = "{\"serverTime\":";
int pattern_lenght;
int end;
string server_time = "";
string time_stamp = "";

string symbol;
string sQty;
string side;
string params;
string message;
string secrete_key;
if(ClosePrice[0] < OpenPrice[0] && ClosePrice[1] > OpenPrice[1] && ClosePrice[1] > HighPrice[0])
  {

// Print("BULLISH ENGULFING CONFIRMED");
//SEND A BUY ORDER TO BINANCE

   WebRequest("GET", url_t, headers_t, 5000, data_t, result_t, response_headers_t);
   time = CharArrayToString(result_t);

   pattern_lenght = StringFind(time,pattern);
   pattern_lenght += StringLen(pattern);
   end = StringFind(time,"}",pattern_lenght + 1);

   server_time = StringSubstr(time,pattern_lenght,end - pattern_lenght);
   time_stamp = "timestamp=" + server_time;

   symbol    = "DOGEUSDT";
   sQty = DoubleToString(6, 8);
   StringReplace(sQty, ",", ".");
   side      = "BUY";

   params = "symbol=" + symbol +
            "&side=" + side +
            "&type=MARKET" +
            "&quoteOrderQty=" + sQty +
            "&" + time_stamp;

   message = params;
   secrete_key = "AbcdefGhmb91TXxKK0Ct51sjh9312345eIdLkRpYThugopIMe4EZVjaOCYabcdef";

Erläuterung:

Eine Anfrage an den Serverzeit-Endpunkt von Binance wird mit den ersten Variablen vorbereitet. Der Binance-API-Pfad, der die aktuelle Serverzeit liefert, ist in der URL angegeben. Da Binance erwartet, dass die Zeitstempel von seinem eigenen Server und nicht von Ihrer lokalen Systemzeit stammen, ist dieser Endpunkt entscheidend. Da für diese Anfrage keine Authentifizierung erforderlich ist, bleibt die Variable Header leer. Die rohe Server-Antwort und die rohen Anfragedaten werden in Zeichen-Arrays gespeichert, und alle Header, die Binance zurückgibt, werden in der Antwort-Header-Variable gespeichert.

Die Anfrage wird an Binance gesendet, wenn die WebRequest-Funktion mit der GET-Methode und der Serverzeit-URL aufgerufen wird. Die aktuelle Serverzeit in Millisekunden ist in der JSON-Nachricht enthalten, mit der Binance antwortet. Das Ergebnis-Array enthält die Antwort in Form von Roh-Bytes. Eine Funktion zur Umwandlung von Byte-Arrays in Zeichenketten wird dann verwendet, um diese Byte-Rohdaten in eine lesbare Zeichenkette umzuwandeln. Die vollständige JSON-Antwort von Binance, einschließlich des Zeitstempels des Servers, ist nun in dieser Zeichenfolge enthalten. Für autorisierte Abfragen ist dieser Zeitstempel nun der erste notwendige Parameter. 

Der Zeitstempel wird dann mithilfe einer Reihe von String-Variablen aus der Serverantwort extrahiert. Der JSON-Schlüssel, den Binance für die Serverzeit verwendet, wird mit einem vorgegebenen Musterstring abgeglichen. Um den Beginn und das Ende des Zeitstempels innerhalb der Antwortnachricht zu überwachen, werden weitere Variablen deklariert. Die extrahierte Serverzeit und der endgültige formatierte Zeitstempelparameter werden beide in leeren Zeichenfolgen gespeichert.

Das Programm sucht nach dem Muster, das den Beginn des Serverzeitfelds im Antwortstring angibt. Nachdem das Muster gefunden wurde, wird seine Länge addiert, sodass die Extraktion genau nach dem Etikett beginnt und nicht erst ab dem Etikett selbst. Das Programm sucht dann nach der schließenden Klammer, die anzeigt, wo der Wert der Serverzeit endet. Nur der numerische Zeitstempelwert ist in der Teilzeichenkette enthalten, die an diesen beiden Stellen extrahiert wird. Wie von Binance angegeben, wird diese Zahl immer noch in Millisekunden angegeben. Danach wird der abgerufene Wert in einen gültigen Abfrage-String-Parameter formatiert, indem ihm das Wort „timestamp=“ vorangestellt wird. Dadurch wird der Zeitstempel-Parameter generiert, den die API-Anfrage verwenden wird.

Die übrigen für einen Auftrag erforderlichen Parameter werden durch die folgenden Variablen definiert. Das Handelssymbol ist bereit, in einer Zeichenkette gehalten zu werden. Für die Menge steht ein weiterer String bereit. Eine wird verwendet, um alle Argumente in einem einzigen Abfrage-String zusammenzufassen, während eine andere für die Orderseite verwendet wird. Die zu signierende Zeichenkette und der geheime Schlüssel, der zur Erstellung der Signatur verwendet wird, werden in zwei zusätzlichen Variablen gespeichert. Das Handelspaar, an das der Auftrag geliefert werden soll, wird durch die Symbolvariable angegeben.

Um dem erwarteten numerischen Format von Binance zu entsprechen, wird die Mengenvariable in eine Zeichenkette mit entsprechender Dezimalformatierung umgewandelt, und jede Kommaformatierung wird durch einen Punkt ersetzt. Der Aktionstyp wird durch die Seitenvariable angegeben. Anschließend werden alle erforderlichen Felder zu einer einzigen Abfragezeichenfolge verkettet, um die Parameterzeichenfolge zu erstellen. Dies umfasst die Menge, die Auftragsart, die Seite, das Symbol und den Zeitstempel. Wie in der API-Definition von Binance vorgeschrieben, wird jedes Argument mit dem Zeichen „&“ verbunden.

Danach wird der Variablen message die gesamte Fragezeichenfolge zugewiesen. Der gesamte Satz von Parametern, der an Binance übermittelt wird, wird durch diese Nachricht dargestellt. Der private API-Schlüssel, der zur Erstellung der Signatur verwendet wird, wird in der Variablen secret key gespeichert. Alles, was zur Erstellung von Signaturen benötigt wird, ist bereits vorhanden. Alle Abfrageparameter sind in der Nachricht enthalten, und die kryptografische Signatur ist mit dem geheimen Schlüssel möglich. Dies ist von entscheidender Bedeutung, da Binance vorschreibt, dass der gesamte Abfrage-String und nicht nur der Zeitstempel zur Erstellung der Signatur verwendet wird. Die Anfrage wird nicht authentifiziert, wenn die Eingabe der Signatur fehlende Parameter enthält.

Beispiel:
uchar uMsg[], uSKey[];
int blockSize = 64;
uchar ipad[], opad[];
uchar innerData[], innerHash[];
uchar outerData[], finalHash[];

string API_KEY = "abcdefkuoeX7gUvCb8nX0Ros4Jsabcdef3kA5nw8e12345udVZwWYHgOjyabcdef";
string headers = "X-MBX-APIKEY: " + API_KEY + "\r\n";

char   result[];
string response_headers;
char   datas[];

string url;
int status;
string server_response;
if(ClosePrice[0] < OpenPrice[0] && ClosePrice[1] > OpenPrice[1] && ClosePrice[1] > HighPrice[0])
  {

// Print("BULLISH ENGULFING CONFIRMED");
//SEND A BUY ORDER TO BINANCE

   WebRequest("GET", url_t, headers_t, 5000, data_t, result_t, response_headers_t);
   time = CharArrayToString(result_t);

   pattern_lenght = StringFind(time,pattern);
   pattern_lenght += StringLen(pattern);
   end = StringFind(time,"}",pattern_lenght + 1);

   server_time = StringSubstr(time,pattern_lenght,end - pattern_lenght);
   time_stamp = "timestamp=" + server_time;

   symbol    = "DOGEUSDT";
   sQty = DoubleToString(6, 8);
   StringReplace(sQty, ",", ".");
   side      = "BUY";

   params = "symbol=" + symbol +
            "&side=" + side +
            "&type=MARKET" +
            "&quoteOrderQty=" + sQty +
            "&" + time_stamp;

   message = params;
   secrete_key = "AbcdefGhmb91TXxKK0Ct51sjh9312345eIdLkRpYThugopIMe4EZVjaOCYabcdef";

   StringToCharArray(message, uMsg, 0, StringLen(message), CP_UTF8);
   StringToCharArray(secrete_key, uSKey, 0, StringLen(secrete_key), CP_UTF8);

   ArrayResize(ipad, blockSize);
   ArrayResize(opad, blockSize);

   for(int i = 0; i < blockSize; i++)
     {
      ipad[i] = uSKey [i] ^ 0x36;
      opad[i] = uSKey [i] ^ 0x5C;
     }

   ArrayCopy(innerData, ipad);
   ArrayCopy(innerData, uMsg, blockSize);
   CryptEncode(CRYPT_HASH_SHA256, innerData, uSKey, innerHash);

   ArrayCopy(outerData, opad);
   ArrayCopy(outerData, innerHash, blockSize);
   CryptEncode(CRYPT_HASH_SHA256, outerData, uSKey, finalHash);

   string signature = "";
   for(int i = 0; i < ArraySize(finalHash); i++)
      signature += StringFormat("%02x", finalHash[i]);

   Print(signature);

   string query_string = "";
   query_string += "&signature=" + signature;

   url = "https://api.binance.com/api/v3/order?" + params + "&signature=" + signature;
   status = WebRequest("POST", url, headers, 5000, datas, result, response_headers);
   server_response = CharArrayToString(result);

   Print(server_response);

  }

Erläuterung:

Der anfängliche Satz von Variablen wird verwendet, um alles für die endgültige API-Anfrage und die Authentifizierung vorzubereiten. Die Nachricht und der geheime Schlüssel werden in zwei Byte-Arrays in einem Rohbyte-Format gespeichert. Dies ist notwendig, da beim kryptografischen Hashing Bytes und keine Zeichenketten verwendet werden. Die Standardblockgröße des SHA256-Algorithmus für HMAC-Operationen ist auf 64 Byte festgelegt. Ein Padding-Array wird für das äußere Padding und ein weiteres für das innere Padding angegeben. Das HMAC-Verfahren besteht aus mehreren wesentlichen Elementen. Zur Speicherung von Hash-Ergebnissen und Zwischendaten während des gesamten Signiervorgangs werden weitere Arrays erstellt. Dank dieser Aufteilung ist es einfacher, die HMAC-Schritte zu verfolgen.

Der geheime Schlüssel und der API-Schlüsselstring werden unabhängig voneinander angegeben. Ihr Konto wird ausschließlich durch den API-Schlüssel in den Headern der Anfrage identifiziert. Sie ist nicht in der Signatur enthalten. Das erforderliche Format des Binance-Header wird verwendet, um die Zeichenkette des Header zu erzeugen, auf die dann ein Zeilenumbruch folgt. Der Auftrags-Request wird mit dieser Kopfzeile übermittelt. Die Antwort des Servers wird dann durch die Deklaration weiterer Variablen bearbeitet. Dazu gehören Variablen, die den endgültigen Antworttext des Servers enthalten, Arrays für Antwort-Rohdaten und Strings für Antwort-Header. Zusätzlich werden die URL, der Statuscode und die endgültige Antwortzeichenfolge für die spätere Verwendung vorbereitet.

Danach werden Byte-Arrays aus den Zeichenfolgen für die Nachricht und den geheimen Schlüssel erstellt. Da die Signaturtechnik auf Byte-Ebene arbeitet, ist dieser Schritt unerlässlich. Um sicherzustellen, dass die Byte-Darstellung dem entspricht, was Binance erwartet, wird die UTF-8-Kodierung verwendet. Sowohl der geheime Schlüssel als auch die Abfragestring-Argumente liegen derzeit in Form von Byte-Rohdaten vor. Die SHA256-Blockgröße wird durch Größenanpassung der inneren und äußeren Padding-Arrays angepasst. Diese Arrays werden anschließend durch eine Schleife gefüllt, die feste hexadezimale Werte mit den geheimen Schlüsselbytes kombiniert. Ein Wert wird für das innere Padding und ein anderer für die äußere Polsterung verwendet. Diese Umwandlung garantiert, dass der geheime Schlüssel sicher in das Hashing-Verfahren einbezogen wird und ist Bestandteil des HMAC-Standards.

Die inneren Daten werden konstruiert, indem das innere Padding kopiert und die Nachrichtenbytes nach der Vorbereitung des Paddings angehängt werden. Der erste Schritt der HMAC-Berechnung wird durch diese kombinierten Daten dargestellt. Der innere Hash wird erstellt, indem ein SHA256-Hash aus diesen inneren Daten erzeugt wird. Der Einfluss des geheimen Schlüssels ist bereits sicher in diesem inneren Hash enthalten. Das äußere Padding wird dann kopiert, und das innere Hash-Ergebnis wird hinzugefügt, um die äußeren Daten zu erstellen. Der HMAC-Prozess verwendet diese zweite Kombination als endgültige Eingabe. Der endgültige Hash wird durch Anwendung eines weiteren SHA256-Hashes auf diese Daten erstellt. Die kryptografische Signatur, die bestätigt, dass die Anfrage mit dem entsprechenden geheimen Schlüssel erstellt wurde, ist dieser letzte Hash.

Der endgültige Hashwert wird in eine lesbare hexadezimale Zeichenkette umgewandelt, da er immer noch in roher Byteform vorliegt. Jedes Byte wird in eine Zeichenfolge eingefügt, nachdem es als zweistelliger hexadezimaler Wert aufbereitet wurde. Die endgültige Zeichenkette hat genau das von Binance geforderte Signaturformat. Bei der Fehlersuche oder Überprüfung kann es von Vorteil sein, sie auszudrucken. Wenn die Signatur vorbereitet ist, wird sie als zusätzlicher Parameter an den Abfrage-String angehängt.

Die Signatur muss laut Binance als Teil der Anfrage-URL und nicht im Header übermittelt werden. Der Binance-Auftrags-Endpunkt, die zuvor generierten Parameter und die generierte Signatur werden dann kombiniert, um die endgültige Anfrage-URL zu erstellen. Eine POST-Request mit der vollständigen signierten URL und dem Authentifizierungs-Header wird mit der WebRequest-Methode an Binance gesendet. Nach der Bearbeitung der Anfrage speichert Binance die Antwort als Rohbytes.


Schlussfolgerung

Dieser Artikel behandelt den gesamten Prozess der Arbeit mit der Binance-API in MQL5, vom Abrufen und Organisieren von Kerzendaten bis zum Identifizieren eines bullischen Engulfing-Musters und dem Senden authentifizierter Handelsaufträge. Wir haben uns damit beschäftigt, wie man signierte Anfragen erstellt, Parameter korrekt behandelt und mit der Funktion WebRequest Live-Trades ausführt. Mit diesem letzten Schritt, Aufträge über die Binance-API zu platzieren, schließt dieser Artikel die API-Serie ab und vervollständigt die Reise von der Marktdatenanalyse bis zur Live-Handelsausführung im MetaTrader 5.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/21061

Beigefügte Dateien |
Python-MetaTrader 5 Strategie-Tester (Teil 01): Handelssimulator Python-MetaTrader 5 Strategie-Tester (Teil 01): Handelssimulator
Das MetaTrader-5-Modul für Python ermöglicht es, Trades bequem über Python in der MetaTrader-5-Anwendung zu eröffnen. Es hat jedoch einen großen Nachteil: Die im MetaTrader-5-Terminal verfügbare Funktion des Strategietesters fehlt. In dieser Artikelserie werden wir ein Framework für das Backtesting Ihrer Handelsstrategien in Python-Umgebungen aufbauen.
Entwicklung des Price Action Analysis Toolkit (Teil 58): Modul zur Analyse der Spannenverengung und Reifegradklassifizierung Entwicklung des Price Action Analysis Toolkit (Teil 58): Modul zur Analyse der Spannenverengung und Reifegradklassifizierung
Aufbauend auf dem vorangegangenen Artikel, in dem das Modul zur Klassifizierung des Marktzustands vorgestellt wurde, konzentriert sich dieser Teil auf die Implementierung der Kernlogik zur Identifizierung und Bewertung von Kompressionszonen. Vorgestellt wird ein MQL5-System zur Erkennung von Range-Kontraktionen und zur Einstufung ihres Reifegrads, das Marktkompression ausschließlich anhand des Kursverlaufs analysiert.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Einführung in MQL5 (Teil 37): Beherrschung von API und WebRequest in MQL5 (XI) Einführung in MQL5 (Teil 37): Beherrschung von API und WebRequest in MQL5 (XI)
In diesem Artikel zeigen wir Ihnen, wie Sie mit MQL5 authentifizierte Anfragen an die Binance-API senden, um Ihren Kontostand für alle Assets abzurufen. Erfahren Sie, wie Sie Ihren API-Schlüssel, die Serverzeit und die Signatur verwenden, um sicher auf Kontodaten zuzugreifen, und wie Sie die Antwort zur späteren Verwendung in einer Datei speichern können.