English
preview
Erstellen eines Dashboards in MQL5 für den RSI-Indikator von mehreren Symbolen und Zeitrahmen

Erstellen eines Dashboards in MQL5 für den RSI-Indikator von mehreren Symbolen und Zeitrahmen

MetaTrader 5Handelssysteme | 23 September 2024, 11:17
74 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In diesem Artikel werden wir Sie durch den Prozess der Erstellung eines Indikator Dashboards des RSI (Relative Strength Index) für mehrere Währungen und Zeitrahmen in der Sprache MetaQuotes Language 5 (MQL5) für den MetaTrader 5 (MT5) geleitet. Dieser umfassende Leitfaden befasst sich mit der Definition, der Funktionsweise und den praktischen Anwendungen eines nutzerdefinierten RSI-Dashboards sowie mit den Schritten, die für dessen Entwicklung mit MetaQuotes Language 5 (MQL5) erforderlich sind.

Ein dynamisches RSI-Dashboard dient als leistungsstarkes Werkzeug für Händler, das eine konsolidierte Ansicht der RSI-Werte über mehrere Symbole und Zeitrahmen hinweg bietet. Dies ermöglicht eine fundiertere Entscheidungsfindung, indem überkaufte oder überverkaufte Marktbedingungen identifiziert werden. Durch die Visualisierung der RSI-Daten in einer einzigen Oberfläche können Händler die Marktbedingungen schnell beurteilen und ihre Strategien entsprechend anpassen.

Wir werden die folgenden Schlüsselbereiche abdecken:

  • Initialisierung des Dashboards: Einrichten der Umgebung, Erstellen von Hauptschaltflächen und Anzeigen von Zeitrahmen und Symbolen.
  • Aktualisierungen in Echtzeit: Implementierung von Funktionen zur dynamischen Berechnung und Anzeige von RSI-Werten, mit Aktualisierungen auf der Grundlage von Live-Marktdaten.
  • Erstellen und Aktualisieren von Schaltflächen: Detaillierte Erläuterung der Funktionen zum Erstellen und Aktualisieren von Schaltflächen, damit das Dashboard nutzerfreundlich und informativ ist.
  • Anpassung und praktische Anwendung: Wie Sie das Dashboard an Ihre individuellen Handelsbedürfnisse anpassen und es in Ihre Handelsstrategie integrieren können.

Am Ende dieses Artikels werden Sie ein gründliches Verständnis dafür haben, wie man ein RSI-Dashboard mit mehreren Symbolen und mehreren Perioden in MQL5 erstellt und verwendet. Dies wird Ihr Handelsinstrumentarium erweitern, Ihre Fähigkeit zur Analyse von Markttrends verbessern und Ihnen letztendlich helfen, fundiertere Handelsentscheidungen zu treffen. Um dies zu erreichen, werden wir die folgenden Themen nutzen:

  1. Übersicht und Elemente Illustration
  2. Implementation in MQL5
  3. Schlussfolgerung

Auf dieser Reise werden wir ausgiebig MetaQuotes Language 5 (MQL5) als unsere grundlegende IDE (Integrated Development Environment) Programmierumgebung in MetaEditor verwenden und die Dateien auf dem MetaTrader 5 (MT5) Trading Terminal ausführen. Daher ist es von größter Bedeutung, dass die oben genannten Versionen vorhanden sind. Lassen Sie uns in die Feinheiten der Entwicklung dieses leistungsstarken Handelsinstruments eintauchen. 


Übersicht und Elemente Illustration

Wir erstellen ein umfassendes Dashboard, das die RSI-Werte über mehrere Handelssymbole und Zeitrahmen hinweg konsolidiert und Händlern ein leistungsstarkes Tool für die Marktanalyse an die Hand gibt. Wir werden alles im Detail erläutern und behandeln, um ein gründliches Verständnis zu gewährleisten. Unsere Entwicklung wird sich auf die folgenden Schlüsselelemente konzentrieren:

  • Initialisierung des Dashboards:

Erstellung der Hauptschaltfläche: Der erste Schritt in unserem Initialisierungsprozess besteht darin, eine zentrale Schaltfläche zu erstellen, die die Basis und den Benchmark des Dashboards angibt. Diese Schaltfläche dient als primäres Steuerelement für das Dashboard und bildet den Mittelpunkt der Nutzeroberfläche. Es wird strategisch am oberen Rand des Armaturenbretts positioniert, damit es leicht zugänglich und gut sichtbar ist.

Schaltflächen der Zeitrahmen: Als Nächstes werden wir Schaltflächen erstellen, die verschiedene Zeitrahmen darstellen, um die anderen Indikatordaten aus verschiedenen Symbolperioden anzuzeigen. Diese Schaltflächen sind horizontal neben der Hauptschaltfläche angeordnet und dienen der Anzeige von RSI-Werten für den jeweiligen Periodenlänge. Jede Zeitrahmen-Schaltfläche ist mit der entsprechenden Periodenabkürzung beschriftet, sodass Händler die Daten verschiedener Zeitrahmen auf einen Blick erkennen und vergleichen können.

  • Schaltflächen der Symbole:

Dynamische Symbolliste: Um einen umfassenden Überblick über den Markt zu bieten, werden wir Schaltflächen für jedes Handelssymbol generieren, das auf der MetaTrader 5-Plattform des Nutzers verfügbar ist und zur Marktbeobachtung hinzugefügt wird. Diese Schaltflächen werden dynamisch erstellt und vertikal unter der Hauptschaltfläche aufgeführt. Das derzeit aktive Handelssymbol wird mit einer bestimmten Farbe (z. B. lime oder green) hervorgehoben, damit es leicht zu erkennen ist. Diese Funktion gewährleistet, dass Händler das aktive Symbol schnell identifizieren und seine RSI-Werte in Echtzeit überwachen können.

Basisschaltfläche: Am unteren Ende der Symbolliste fügen wir eine Basisschaltfläche hinzu, die sich über die Breite aller Zeitrahmenschaltflächen zusammen erstreckt. Diese Schaltfläche erhält den Namen des Dashboards bzw. dessen Titel, kann aber auch dazu verwendet werden, den Status des aktuellen Symbolsignals anzuzeigen oder als Zusammenfassung oder Fußzeile für die Symbolliste zu dienen. Sie sorgt für eine klare Abgrenzung zwischen den Symbolschaltflächen und der Anzeige des RSI-Werts und gewährleistet, dass das Dashboard übersichtlich und leicht zu navigieren ist.

  • Aktualisierungen in Echtzeit:

RSI-Berechnung: Um sicherzustellen, dass das Dashboard genaue und aktuelle Informationen liefert, berechnen wir die RSI-Werte für jedes Symbol und jeden Zeitrahmen in Echtzeit mit Hilfe integrierter MQL5-Funktionen. Diese Funktion berechnet den RSI auf der Grundlage der Schlusskurse des ausgewählten Zeitrahmens und stellt damit einen wichtigen Indikator für die Marktdynamik dar. Die RSI-Werte werden in einem Array gespeichert und bei jedem Tick aktualisiert, um die neuesten Marktdaten wiederzugeben.

Dynamische Anzeige: Die berechneten RSI-Werte werden dynamisch auf den entsprechenden Schaltflächen angezeigt. Um die visuelle Klarheit zu verbessern, werden wir ein Farbkodierungsschema einführen, das auf vordefinierten RSI-Schwellenwerten basiert. Liegt der RSI-Wert unter 30, was auf einen überverkauften Zustand hinweist, wechselt die Hintergrundfarbe der Schaltfläche auf grün. Wenn der RSI-Wert über 70 liegt, was auf einen überkauften Zustand hindeutet, wechselt die Hintergrundfarbe zu Rot. Bei RSI-Werten zwischen 30 und 70 bleibt die Hintergrundfarbe weiß, was auf einen neutralen Zustand hinweist. Diese dynamische Anzeige ermöglicht es den Händlern, den Marktstatus schnell zu beurteilen und fundierte Handelsentscheidungen zu treffen.

Zur Veranschaulichung des gesamten Prozesses sehen Sie hier, was wir am Ende haben wollen.

ÜBERSICHT BILD

Um den gesamten Entwicklungsprozess zu veranschaulichen, werden wir jedes Element in detaillierte Schritte unterteilen und mit Codeausschnitten und Erklärungen versehen. Am Ende dieses Artikels werden Sie ein voll funktionsfähiges RSI-Dashboard haben, das Sie anpassen und in Ihre Handelsstrategie integrieren können.


Implementation in MQL5

Der Indikator Dashboard wird auf einem Expert Advisor basieren. Um einen Expert Advisor (EA) zu erstellen, klicken Sie auf Ihrem MetaTrader 5-Terminal auf die Registerkarte Tools und aktivieren Sie MetaQuotes Language Editor, oder drücken Sie F4 auf Ihrer Tastatur. Alternativ können Sie auch auf das IDE-Symbol (Integrated Development Environment) in der Symbolleiste klicken. Dadurch wird die Umgebung des MetaQuotes-Editors geöffnet, die das Schreiben von Handelsrobotern, technischen Indikatoren, Skripten und Funktionsbibliotheken ermöglicht.

IDE

Sobald der MetaEditor geöffnet ist, navigieren Sie in der Symbolleiste zur Registerkarte „Datei“ und wählen Sie „Neue Datei“, oder drücken Sie einfach die Tastenkombination STRG + N, um ein neues Dokument zu erstellen. Alternativ können Sie auch auf das Symbol New auf der Registerkarte Werkzeuge klicken. Daraufhin erscheint ein Popup-Fenster des MQL-Assistenten.

NEUER EA

Markieren Sie in dem sich öffnenden Assistenten die Option Expert Advisor (Vorlage bzw. template) und klicken Sie auf Weiter (Next).

MQL WIZARD

Geben Sie in den allgemeinen Eigenschaften des Expertenberaters unter dem Abschnitt Name den Dateinamen Ihres Experten an. Beachten Sie, dass Sie den Backslash vor dem Namen des EA verwenden, um einen Ordner anzugeben oder zu erstellen, wenn er nicht existiert. Hier haben wir zum Beispiel standardmäßig „Experts\“. Das bedeutet, dass unser EA im Ordner Experts erstellt wird und wir ihn dort finden können. Die anderen Abschnitte sind ziemlich einfach, aber Sie können dem Link am Ende des Assistenten folgen, um zu erfahren, wie der Prozess genau abläuft.

NEUER EA

Nachdem Sie den gewünschten Namen des Expert Advisors eingegeben haben, klicken Sie auf Weiter, dann auf Weiter und schließlich auf Fertig stellen. Nachdem wir all das getan haben, sind wir nun bereit, unser Indikator-Dashboard zu programmieren.

Zunächst müssen wir eine Funktion für die zu erstellenden Schaltflächen erstellen. Dies ist von großem Nutzen, da es uns ermöglicht, dieselbe Funktion bei der Erstellung ähnlicher Merkmale wiederzuverwenden, anstatt den gesamten Prozess bei der Erstellung ähnlicher Objekte wiederholen zu müssen. Außerdem sparen wir dadurch eine Menge Zeit und Platz, da der Prozess schnell und unkompliziert ist und die Codeschnipsel kurz.

Um die Schaltflächen zu erstellen, werden wir eine Funktion erstellen, die 11 Argumente oder Parameter benötigt.

//+------------------------------------------------------------------+
//| Function to create a button                                      |
//+------------------------------------------------------------------+
bool createButton(string objName, string text, int xD, int yD, int xS, int yS,
   color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE,
   string font = "Arial Rounded MT Bold"
) {

...

}

Die Funktionssignatur veranschaulicht alles. Es handelt sich um eine boolesche Funktion mit dem Namen „createButton“, was bedeutet, dass sie bei Erfolg bzw. Misserfolg zwei boolesche Flags, true oder false, zurückgibt. Zum besseren Verständnis der Parameter werden diese im Folgenden einzeln erläutert.

  • objName (string): Dieser Parameter gibt den Namen des Schaltflächenobjekts an. Jede Schaltfläche muss einen eindeutigen Namen haben, um sie von anderen Objekten im Chart unterscheiden zu können. Dieser Name wird verwendet, um bei Aktualisierungen und Änderungen auf die Schaltfläche zu verweisen.
  • text (string): Legt den Text fest, der auf der Schaltfläche angezeigt werden soll. Dies kann eine beliebige Zeichenfolge sein, z. B. „RSI“, „BUY“ oder eine andere für den Zweck der Schaltfläche relevante Bezeichnung.
  • xD (int): Der Parameter gibt den horizontalen Abstand der Schaltfläche von der angegebenen Ecke des Charts an. Die Einheit ist in Pixel.
  • yD (int): Ähnlich wie bei xD definiert dieser Parameter den vertikalen Abstand der Schaltfläche von der angegebenen Ecke des Charts.
  • xS (int): Der Parameter gibt die Breite der Schaltfläche in Pixeln an. Er bestimmt, wie breit die Schaltfläche im Chart erscheint.
  • yS (int): Definiert die Höhe der Schaltfläche in Pixeln. Sie bestimmt, wie hoch die Schaltfläche im Chart erscheint.

ABSTAND UND GRÖSSE

  • clrTxt (color): Mit diesem Parameter wird die Farbe des auf der Schaltfläche angezeigten Textes festgelegt. Die Farbe kann mit den vordefinierten Farbkonstanten in MQL5 angegeben werden, wie clrBlack, clrWhite, clrRed, etc.
  • clrBg (color): Damit wird die Hintergrundfarbe der Schaltfläche festgelegt. Sie wird ebenfalls über vordefinierte Farbkonstanten festgelegt und bestimmt die Füllfarbe der Schaltfläche.
  • fontSize (int): Dieser optionale Parameter gibt die Größe der Schriftart an, die für den Text der Schaltfläche verwendet wird. Der Standardwert ist 12, wenn er nicht angegeben wird. Die Schriftgröße bestimmt die Größe des auf der Schaltfläche angezeigten Textes.
  • clrBorder (color): Dieser andere optionale Parameter legt die Farbe des Rahmens der Schaltfläche fest. Der Standardwert ist „clrNONE“, wenn er nicht angegeben wird, d. h. es wird keine Rahmenfarbe angewendet. Falls angegeben, kann die Farbe des Rahmens die Sichtbarkeit und Ästhetik der Schaltfläche verbessern.
  • font (string): Dieser andere optionale Parameter definiert die Schriftart, die für den Text der Schaltfläche verwendet wird. Der Standardwert ist „Arial Rounded MT Bold“, wenn er nicht angegeben wird. Die Schriftart bestimmt den Stil des Textes, der auf der Schaltfläche angezeigt wird.

In der Funktionssignatur sollten Sie bemerkt haben, dass einige der Argumente bereits auf einen Wert initialisiert sind. Der Initialisierungswert stellt den Standardwert dar, der diesem Parameter zugewiesen wird, falls er während des Funktionsaufrufs ignoriert wird. Unsere Standard-Rahmenfarbe ist beispielsweise „none“, d. h., wenn der Farbwert während des Funktionsaufrufs nicht angegeben wird, wird für den Rand unseres Rechtecks keine Farbe verwendet. 

Innerhalb des Funktionskörpers, der von geschweiften Klammern ({}) eingerahmt wird, definieren wir unsere Prozeduren zur Erstellung von Objekten. 

   // Attempt to create the button
   if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
      Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails
      return (false); // Return false if creation fails
   }

Wir beginnen mit einer if-Anweisung, um zu prüfen, ob das Objekt nicht erstellt wurde. Die Funktion ObjectCreate, ein boolescher Wert mit 6 Argumenten, wird verwendet. Diese Funktion erstellt ein Objekt mit dem angegebenen Namen, Typ und den Anfangskoordinaten im angegebenen Teilfenster des Charts. Zunächst geben wir das Chartfenster an, 0 bedeutet, dass das Objekt im Hauptfenster erstellt werden soll. Dann geben wir den Objektnamen an. Dies ist der Name, der einem bestimmten Objekt eindeutig zugewiesen wird. Das zu erstellende Objekt ist vom Typ „OBJ_BUTTON“, d.h. es handelt sich um ein Objekt zur Erstellung und Gestaltung des nutzerdefinierten Indikator-Dashboards. Anschließend wird das Fenster angegeben, 0 für das aktuelle Chartfenster. Schließlich geben wir die Zeit- und Preiswerte als Null (0) an, da wir sie nicht mit dem Chart, sondern mit den Koordinaten des Chartfensters verbinden werden. Die Zuordnung wird mit Hilfe von Pixeln festgelegt.

Wenn die Erstellung des Objekts fehlschlägt und die Funktion ObjectCreate schließlich false zurückgibt, ist es offensichtlich sinnlos, weiterzumachen, wir kehren mit einem Fehler zurück. In diesem Fall informieren wir über den Fehler, indem wir ihn neben dem Fehlercode in das Journal drucken und false zurückgeben. Es könnte ein vorheriger Fehler vorliegen, und um den neuesten Fehler zu erhalten, müssen wir den vorherigen Fehler löschen. Dies wird durch den Aufruf der Funktion ResetLastError erreicht, die eine eingebaute MQL5-Funktion ist, unmittelbar vor der Logik der Objekterstellung.

   ResetLastError(); // Reset the last error code

Der Zweck der Funktion ist es, den Wert der Funktion GetLastError, die den Fehlercode der letzten Operation, bei der ein Fehler aufgetreten ist, abfragt, auf Null zu setzen. Durch den Aufruf dieser Funktion wird sichergestellt, dass alle vorherigen Fehlercodes gelöscht werden, bevor mit den nächsten Operationen fortgefahren wird. Dieser Schritt ist wichtig, weil er es uns ermöglicht, neue Fehler unabhängig von früheren Fehlerzuständen zu behandeln.

Wenn wir bis zu diesem Punkt nicht zurückkehren, bedeutet dies, dass wir das Objekt erstellt haben, und wir können mit der Aktualisierung der Eigenschaften des Objekts fortfahren. Eine eingebaute Funktion „ObjectSet...“ setzt den Wert der entsprechenden Objekteigenschaft. Die Objekteigenschaft muss vom Typ datetime, integer, color, boolean oder character sein.

   // Set button properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
   ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
   ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property
   ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected

Wir wollen uns auf die erste Eigenschaftslogik konzentrieren.

   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance

Hier verwenden wir die eingebaute Funktion ObjectSetInteger und übergeben die entsprechenden Parameter. Die Parameter sind wie unten beschrieben.

  • Chart id: Dies ist die ID des Charts. „0“ bezieht sich auf den aktuellen Chart (Chart-ID). Wir passen die Eigenschaften eines Objekts in diesem Chart an.
  • Name: Dies ist der Name des Objekts: „objName“ steht für den eindeutigen Namen, der dem Rechteckbeschriftungsobjekt zugewiesen wurde.
  • Property id: Dies ist die ID der Objekteigenschaft und ihr Wert kann einer der Werte der Enumeration „ENUM_OBJECT_PROPERTY_INTEGER“ sein. „OBJPROP_XDISTANCE“ gibt an, dass wir die Eigenschaft X-Distanz ändern.
  • Property value: Dies ist der Eigenschaftswert. Der Wert, der „xD“ zugewiesen wird, bestimmt, wie weit rechts (oder links, falls negativ) die obere linke Ecke unserer Rechteckbeschriftung horizontal vom linken Rand des Charts positioniert wird.

Auch die anderen Eigenschaften werden nach demselben Schema festgelegt. „OBJPROP_YDISTANCE“ konfiguriert die Y-Abstandseigenschaft der Rechteckbeschriftung. Der Wert „yD“ legt fest, wie weit die linke obere Ecke der Rechteckbeschriftung vertikal vom oberen Rand des Charts entfernt sein soll. Mit anderen Worten, sie steuert die vertikale Platzierung der Beschriftung innerhalb des Chartbereichs. Damit wird der Y-Abstand von der Ecke festgelegt. Mit „OBJPROP_XSIZE“ und „OBJPROP_YSIZE“ wird die Breite bzw. Höhe des Rechtecks festgelegt. 

Um unser Objekt zu positionieren, verwenden wir die Eigenschaft „OBJPROP_CORNER“, um die Ecke zu bestimmen, in der sich unser Objekt im Chartfenster befinden soll.

   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position

Es kann nur 4 Eigenschaftsarten geben:

  • CORNER_LEFT_UPPER: Der Ankerpunkt der Koordinaten befindet sich in der oberen linken Ecke des Charts.
  • CORNER_LEFT_LOWER: Der Ankerpunkt der Koordinaten befindet sich in der unteren linken Ecke des Charts.
  • CORNER_RIGHT_LOWER: Der Ankerpunkt der Koordinaten befindet sich in der unteren rechten Ecke des Charts.
  • CORNER_RIGHT_UPPER: Der Ankerpunkt der Koordinaten befindet sich in der oberen rechten Ecke des Charts.

In einer fotografischen Darstellung haben wir das hier.

OBJEKT-ECKEN

Der Rest der Eigenschaften ist einfach zu handhaben. Zum besseren Verständnis haben wir sie mit Kommentaren versehen. Dann wird das Chart einfach neu gezeichnet, damit die Änderungen automatisch wirksam werden, ohne dass auf eine Änderung der Kursnotierungen oder der Chartereignisse gewartet werden muss. 

   ChartRedraw(0); // Redraw the chart to reflect the new button

Schließlich geben wir true zurück, was bedeutet, dass die Erstellung und Aktualisierung der Objekteigenschaften erfolgreich war.

   return (true); // Return true if creation is successful

Der vollständige Funktionscode, der für die Erstellung eines Schaltflächenobjekts im Chartfenster verantwortlich ist, lautet wie folgt.

//+------------------------------------------------------------------+
//| Function to create a button                                      |
//+------------------------------------------------------------------+
bool createButton(string objName, string text, int xD, int yD, int xS, int yS,
   color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE,
   string font = "Arial Rounded MT Bold"
) {
   ResetLastError(); // Reset the last error code
   // Attempt to create the button
   if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
      Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails
      return (false); // Return false if creation fails
   }
   // Set button properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
   ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
   ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property
   ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected
   
   ChartRedraw(0); // Redraw the chart to reflect the new button
   return (true); // Return true if creation is successful
}

Da wir nun die Funktion haben, die wir zum Erstellen einer Schaltfläche benötigen, können wir sie zum Erstellen des Indikators Dashboard verwenden. Wir brauchen Namen für die Objekte und um die Interaktion der Objektnamen einfach zu verwalten, ist es viel einfacher, Makros zu definieren.

// Define button identifiers and properties
#define BTN1 "BTN1"

Wir verwenden das Schlüsselwort #define, um ein Makro mit dem Namen „BTN1“ und dem Wert „BTN1“ zu definieren, um den Namen unserer Hauptschaltfläche zu speichern, anstatt den Namen bei jeder Erstellung des Schaltflächenabschnitts erneut eingeben zu müssen, was uns viel Zeit spart und die Gefahr einer falschen Namensgebung verringert. Grundsätzlich werden Makros also zur Textersetzung während der Kompilierung verwendet.

Auf ähnliche Weise definieren wir andere Makros, die wir verwenden werden. 

#define Desc "Desc "
#define Symb "Symb "
#define Base "Base "
#define RSI "RSI "
#define XS1 90
#define XS2 100
#define YS1 25
#define clrW clrWhite
#define clrB clrBlack
#define clrW_Gray C'230,230,230'

Hier verwenden wir das Makro „Desc“ als Präfix für die Namen der Schaltflächen, die verschiedene Zeitrahmen beschreiben. Dies ermöglicht uns, eindeutige Namen für diese Beschreibungsschaltflächen zu erzeugen, indem wir Indizes und zusätzliche Zeichenfolgen anhängen, wie „Desc0“, „Desc1“, usw. In ähnlicher Weise wird das Makro „Symb“ als Präfix für die Namen der Schaltflächen verwendet, die verschiedene Handelssymbole repräsentieren, und hilft uns, eindeutige Bezeichnungen wie „Symb0“, „Symb1“ usw. zu erstellen. Das Makro „Base“ dient als Präfix für den Namen der Basisschaltfläche und sorgt so für eine klare und einheitliche Namenskonvention für unsere Dashboard-Komponenten. Um RSI-bezogene Schaltflächen zu handhaben, verwenden wir das Makro „RSI“, das eindeutige Bezeichnungen für Schaltflächen gewährleistet, die RSI-Werte für verschiedene Symbole und Zeitrahmen anzeigen.

Bei den Abmessungen legt „XS1“ die Breite für bestimmte Schaltflächen fest, während „XS2“ und „YS1“ die Breite bzw. Höhe für andere Schaltflächen angeben und so die Größe unserer GUI-Elemente standardisieren. Wir definieren Farbmakros, „clrW“ und „clrB“, damit wir uns in unserem Code bequem auf weiße und schwarze Farben beziehen können. MQL5 hat vordefinierte Farben und das ist es, was wir zuweisen und worauf wir uns beziehen, wenn wir „clrWhite“ für eine weiße Farbe verwenden; die Webfarben.

Farben Abschnitt 1:

FARBBEREICH 1

Farben 2:

FARBBEREICH 2

Zusätzlich definieren wir eine nutzerdefinierte graue Farbe als „clrW_Gray“ C'230,230,230', die als Hintergrund- oder Rahmenfarbe verwendet werden kann, um einen einheitlichen visuellen Stil im gesamten Dashboard zu gewährleisten. Um eine bessere Kontrolle über die Farben zu erhalten, stellen wir das letzte Makro im Literals-Format dar. Diese hat das Format C'000.000.000', wobei die dreifachen Nullen eine beliebige Zahl zwischen 0 und 255 sein können. Das verwendete Format ist RGB (Rot, Grün, Blau). Die drei Werte stehen für die Rot-, Grün- bzw. Blaukomponente auf einer Skala von 0 bis 255. Die Zahl 230.230.230 entspricht also einem fast weißen Farbton.

Wir müssen bestimmte Zeitrahmen für die Symbole festlegen, die wir in unserem Dashboard verwenden wollen. Wir müssen sie also speichern, und das geht am einfachsten in Arrays, wo sie leicht zugänglich sind. 

// Define the timeframes to be used
ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1};

Wir definieren ein statisches Array des Typs ENUM_TIMEFRAMES, um die Zeitrahmen festzulegen, die in unserem Dashboard verwendet werden sollen. Wir benennen das Array als „periods“ und nehmen die folgenden spezifischen Zeitrahmen auf: PERIOD_M1 (1 Minute), PERIOD_M5 (5 Minuten), PERIOD_H1 (1 Stunde), PERIOD_H4 (4 Stunden) und PERIOD_D1 (1 Tag). Durch die Auflistung dieser Zeitrahmen im Feld „periods“ stellen wir sicher, dass unser Dashboard die RSI-Werte für jeden dieser unterschiedlichen Zeitrahmen anzeigt und so einen umfassenden Überblick über die Markttrends über mehrere Zeitrahmen hinweg bietet. Auf diese Weise können wir über das Array iterieren und unsere Berechnungen und Schaltflächenerstellungen später einheitlich für jeden angegebenen Zeitrahmen anwenden. Die Datentypvariable, die zur Definition des Arrays verwendet wird, ist eine Enumeration, die aus allen im MetaTrader 5 (MT5) verfügbaren Zeitrahmen besteht. Sie können jedes beliebige Mittel verwenden, solange es ausdrücklich in Anspruch genommen wird. Hier finden Sie eine Liste aller Zeitrahmen, die Sie verwenden können.

ENUMERATION DER ZEITRAHMEN

Schließlich müssen wir noch in der globalen Variablen ein Indikator-Handle erstellen, das unseren Indikator enthält, sowie ein Array, in dem wir die Indikatordaten für die verschiedenen Zeitrahmen und Symbole speichern. Dies wird durch den folgenden Codeschnipsel erreicht.

// Global variables
int handleName_Id; // Variable to store the handle ID for the RSI indicator
double rsi_Data_Val[]; // Array to store the RSI values

Wir deklarieren eine Variable vom Typ Integer mit dem Namen „handleName_Id“, um die Handle-ID für den RSI-Indikator zu speichern. Wenn wir einen RSI-Indikator für ein bestimmtes Symbol und einen bestimmten Zeitrahmen erstellen, wird er eine eindeutige Handle-ID zurückgeben. Die ID wird dann im Indikator-Handle gespeichert, sodass wir bei späteren Operationen auf diesen spezifischen RSI-Indikator verweisen können, z. B. um seine Werte für weitere Analysen abzurufen. An dieser Stelle kommt das zweite Variablenfeld ins Spiel. Wir definieren ein dynamisches Array vom Typ „double“ namens „rsi_Data_Val“, um die vom Indikator erhaltenen RSI-Werte zu speichern. Wenn wir die RSI-Daten abrufen, werden die Werte in dieses Array kopiert. Indem wir dieses Array zu einer globalen Variablen machen, stellen wir sicher, dass die RSI-Daten im gesamten Programm zugänglich sind, sodass wir die Werte für Echtzeit-Updates verwenden und sie auf unseren Dashboard-Schaltflächen anzeigen können.

Unser Dashboard wird zunächst im Initialisierungsabschnitt des Expert Advisors erstellt. Schauen wir uns also an, was der Initialisierungs-Ereignisbehandlung tut.

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

...
   
   return(INIT_SUCCEEDED); // Return initialization success
}

Die Funktion OnInit ist eine Ereignisbehandlung, die bei der Initialisierungsinstanz des Experten aufgerufen wird, um gegebenenfalls notwendige Initialisierungen vorzunehmen. Es dient dazu, alle notwendigen Ersteinrichtungsaufgaben durchzuführen, um sicherzustellen, dass der Expert Advisor korrekt funktioniert. Dazu gehören das Erstellen von Elementen der Nutzeroberfläche, das Initialisieren von Variablen und das Einrichten der notwendigen Bedingungen, damit das Programm während seiner Laufzeit ordnungsgemäß funktioniert. In unserem Fall werden wir es verwenden, um die Dashboard-Elemente zu initialisieren.

Anschließend rufen wir die Funktion auf, um eine Schaltfläche zu erstellen, indem wir ihren Namen eingeben und ihre Parameter angeben.

   // Create the main button for the pair with specific properties
   createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray);

In diesem Fall lautet der Name unserer Schaltfläche „BTN1“, wie in der Makrodefinition angegeben. Der zweite Parameter wird verwendet, um den Wortlaut oder den Text anzugeben, der in der Nähe der Schaltflächen angezeigt werden soll. Unser Abstand entlang der x-Achse, der Zeit- und Datumsskala, von der rechten oberen Ecke des Chartfensters beträgt 600 Pixel, und der Abstand entlang der y-Achse, der Preisskala, beträgt 50 Pixel. Die Breite wird aus dem bereits vordefinierten Makro übernommen, damit die nachfolgenden Schaltflächen leichter referenziert werden können. Die Breite ist „XS1“, das sind 90 Pixel, und die Höhe ist „YSI“, das sind jeweils 25 Pixel. Wir wählen als Textfarbe „clrW“, also weiß, mit einem grauen Hintergrund, einer Schriftgröße von 15 und einer grauen Umrandung der Schaltfläche. Um die Pixel annähernd zu erfassen, können Sie das Chart auf 0 verkleinern, und die Anzahl der Balken zwischen zwei Fadenkreuzkoordinaten ist gleich der Anzahl der Pixel auf der horizontalen Skala. Ein Beispiel, das ist gemeint:

FADENKREUZ-PIXEL

Der andere Parameter wurde weggelassen, was bedeutet, dass der Standardwert automatisch übernommen wird, d. h. der Schriftname lautet „Arial Rounded MT Bold“. Nach dem Kompilieren ergibt sich das folgende Bild.

PAIR-KNOPF

Selbst wenn wir alle Parameter mit den Standardwerten wie unten angeben würden, wären die Ergebnisse immer die gleichen.

   // Create the main button for the pair with specific properties
   createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray, "Arial Rounded MT Bold");

Als Nächstes wollen wir Schaltflächen für jeden vordefinierten Zeitrahmen erstellen, um die entsprechenden RSI-Informationen (Relative Strength Index) anzuzeigen. Wir können dies statisch tun, indem wir die Funktion aufrufen, um die Schaltflächen für jedes Element zu erstellen, das wir erstellen wollen, aber das würde unseren Code nur sehr lang und mühsam machen. Wir werden also ein dynamisches Format verwenden, das uns hilft, die Schaltflächen in überwachten Iterationen zu erstellen.

   // Loop to create buttons for each timeframe with the corresponding RSI label
   for(int i = 0; i < ArraySize(periods); i++) {
      createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray);
   }

Wir starten eine for-Schleife, die das Array „periods“ durchläuft, das die angegebenen Zeitrahmen enthält, die wir zuvor definiert und gefüllt haben. Wir verwenden die Funktion ArraySize, um sicherzustellen, dass die Schleife alle Elemente im Array abdeckt. Die Funktion ist einfach und benötigt nur ein einziges Argument. Sie gibt einfach die Anzahl der Elemente eines ausgewählten Arrays zurück, in unserem Fall „periods“.  Innerhalb der Schleife rufen wir die Funktion „createButton“ auf, um für jeden Zeitrahmen eine Schaltfläche zu erstellen. Der Name der Schaltfläche wird durch Verkettung des Makros „Desc“ (definiert als „Desc “) mit dem Index „i“ gebildet, der mit der Funktion IntegerToString in eine Zeichenkette umgewandelt wird, sodass jede Schaltfläche einen eindeutigen Namen erhält, z. B. „Desc 0“, „Desc 1“ usw. Die Funktion konvertiert einen Wert vom Typ Integer in eine Zeichenkette mit einer bestimmten Länge und gibt die erhaltene Zeichenkette zurück. Sie benötigt 3 Eingabeparameter, wobei die letzten 2 optional sind. Der erste Parameter ist die zu konvertierende Nummer, in unserem Fall der Index „i“. Wir verwenden eine nutzerdefinierte Funktion, um die Beschriftung der Schaltfläche zu generieren, die Funktion „truncPrds“, die die String-Darstellung des Zeitrahmens in ein besser lesbares Format abschneidet (z. B. „M1“, „M5“) und „RSI 14“ anhängt, um anzuzeigen, dass diese Schaltfläche den RSI-Wert mit einem Periodenlänge von 14 anzeigt. Das Codeschnipsel der Funktion lautet wie folgt:

// Function to truncate the ENUM_TIMEFRAMES string for display purposes
string truncPrds(ENUM_TIMEFRAMES period) {
   // Extract the timeframe abbreviation from the full ENUM string
   string prd = StringSubstr(EnumToString(period), 7);
   return prd; // Return the truncated string
}

Die Funktion nimmt eine Periode vom Typ ENUM_TIMEFRAMES als Parameter und beginnt mit der Konvertierung des ENUM-Wertes in seine Text-Darstellung mit der Funktion EnumToString. Dies führt in der Regel zu einer Zeichenfolge, die ein Präfix enthält, das wir für die Anzeige nicht benötigen. Um diesen unnötigen Teil zu entfernen, verwenden wir die Funktion StringSubstr für eine Teilzeichenkette ab dem 7. Zeichen. Dadurch wird die Zeichenfolge in eine kürzere, besser lesbare Form gebracht, die für die Anzeige in der Nutzeroberfläche geeignet ist. Schließlich geben wir die abgeschnittene Zeichenkette zurück, die eine saubere und prägnante Darstellung des Zeitrahmens liefert, die für die Beschriftung von Schaltflächen in unserem Dashboard verwendet werden kann. Um zu verstehen, warum wir diese Funktion brauchen, hier eine Illustration.

Logik übernommen:

      Print("BEFORE: ",EnumToString(periods[i]));
      Print("AFTER: ",truncPrds(periods[i]));

Die Druckanweisungen.

ABGESCHNITTENE ZEITRAHMEN

Sie können nun deutlich sehen, dass die nicht abgeschnittenen Zeitrahmen länger sind als die abgeschnittenen und dass sie die unnötigen 7 Zeichen „PERIOD_“ einschließlich des Unterstrichs enthalten, die wir letztendlich entfernen.

Die x-Koordinate jeder Schaltfläche wird dynamisch berechnet, ausgehend von einem Anfangswert von 600 - „XS1“ - und durch Subtraktion von „XS2“ (der Breite der Schaltfläche) multipliziert mit dem Index „i“ angepasst. Durch diese Positionierung wird sichergestellt, dass jede Schaltfläche links von der vorherigen platziert wird, wodurch eine horizontale Ausrichtung entsteht. Die y-Koordinate ist auf 50 Pixel vom oberen Rand des Charts festgelegt, um eine einheitliche vertikale Position für alle Zeitrahmenschaltflächen zu gewährleisten. Die Abmessungen der Schaltflächen werden dann mit den Werten festgelegt, die durch die Makros „XS2" für die Breite (100 Pixel) und „YS1" für die Höhe (25 Pixel) definiert sind. Außerdem setzen wir die Textfarbe der einzelnen Schaltflächen auf „clrWhite", eine weiße Farbe, die Hintergrundfarbe auf grau, die Schriftgröße auf 13 und schließlich die Rahmenfarbe auf grau. Nach dem Kompilieren ergibt sich folgendes Bild.

ZEITRAHMEN GRAU

Zur Veranschaulichung: Die Hintergrundfarbe könnte für Sie nicht angenehm sein. Sie können das verwenden, was Sie für richtig halten. Alles, was Sie tun müssen, ist, die Farben nach Ihrem Geschmack zu verändern. Wenn Sie zum Beispiel einen blauen Hintergrund und einen schwarzen Rand haben möchten, können Sie den Code wie folgt ändern.

      createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrDodgerBlue, 13, clrBlack);

Hier haben wir die Hintergrundfarbe in Dodger-Blau und die Rahmenfarbe in Schwarz geändert. Nach dem Kompilieren ergibt sich folgendes Bild:

ZEITRAHMEN VERÄNDERTE FARBEN

Beachten Sie, wie die Schaltflächen schick und ästhetisch ansprechend werden. Um die Konsistenz dieses Artikels zu wahren, verwenden wir jedoch Standardfarben, keine schreienden. Wir werden die auffallende Farben verwenden, wenn wir später im Artikel gültige Signale erstellen und identifizieren.

Als Nächstes müssen wir eine weitere Serie von Schaltflächen mit vertikalen Symbolen erstellen, die wiederum das Format der dynamischen Darstellung übernehmen. Für die Symbole müssen wir keine spezifischen Symbole in einem Array definieren und sie für die Visualisierung verwenden. Wir können automatisch auf die Symbole zugreifen, die der Broker zur Verfügung stellt. Um dies zu erreichen, verwenden wir eine for-Schleife, die alle vom Makler bereitgestellten Symbole durchläuft und bei Bedarf die erforderlichen Symbole auswählt.

   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(true); i++) {

   ...

   }

Um die vom Broker zur Verfügung gestellten Symbole zu erhalten, verwenden wir eine eingebaute MQL5-Funktion SymbolsTotal. Die Funktion gibt die Anzahl der verfügbaren (in der Marktübersicht ausgewählten oder aller) Symbole zurück. Sie benötigt nur einen einzigen booleschen Eingabeparameter, der, wenn er auf true gesetzt wird, die Anzahl der in der Marktübersicht ausgewählten Symbole zurückgibt. Wenn der Wert false ist, wird die Gesamtzahl aller Symbole zurückgegeben. Um dies zu verstehen, drucken wir die Werte aus, wenn der Eingabeparameter der Funktion auf false gesetzt ist.

   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(false); i++) {
      Print("Index ",i,": Symbol = ",SymbolName(i,false));

      ...

   }

In der for-Schleife setzen wir das Flag des ausgewählten Wertes auf false, damit wir Zugriff auf die gesamte Symbolliste erhalten. In der Druckanweisung haben wir eine andere Funktionsversion von SymbolName verwendet, um den Namen des Symbols in der Liste nach Position zu ermitteln. Der zweite Parameter der Funktion gibt den Abfragemodus auf der Grundlage der Auswahlkriterien für Marktbeobachtungssymbole an. Wenn der Wert true ist, wird das Symbol aus der Liste der in der Marktübersicht ausgewählten Symbole genommen. Wenn der Wert false ist, wird das Symbol aus der allgemeinen Liste genommen. Nach dem Kompilieren ergibt sich folgendes Bild. 

ALLE SYMBOLE 1

Fortsetzung.

ALLE SYMBOLE 2

Sie können sehen, dass alle Symbole ausgewählt sind. In diesem Fall werden 396 Symbole ausgewählt und gedruckt. Stellen Sie sich nun einen Fall vor, in dem Sie alle Symbole auf dem Chart anzeigen. Das ist zu viel, oder? Sie passen nicht in ein einziges Schaubild, und wenn Sie es versuchen, ist die Schrift so klein, dass Sie die Symbole nicht gut erkennen können oder dass sie Ihr Schaubild nur verschmutzen. Außerdem sind nicht einmal alle Symbole für Sie von Nutzen. An dieser Stelle können Sie sich überlegen, ob Sie nur einige wenige, die oberste Priorität haben, auswählen und den Rest weglassen. Die Auswahl der besten Paare Ihrer Favoriten finden Sie in der Rubrik Marktbeobachtung. Hier können Sie Ihre Lieblingssymbole platzieren, um die Kursnotierungen zu sehen und ihre Bewegungen auf einen Blick zu verfolgen. Aus dieser Marktbeobachtung werden wir also die Anzeigesymbole auswählen. Um dies zu ermöglichen, setzen wir den Wert der beiden Funktionen auf true.

   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(true); i++) {
      Print("Index ",i,": Symbol = ",SymbolName(i,true));

      ...

   }

Nach dem Kompilieren ergibt sich folgendes Bild:

AUSDRUCK DER MARKTBEOBACHTUNGSSYMBOLE

Beachten Sie, dass die Symbole auf der Marktuhr, die 12 sind, die gleichen sind, die in der gleichen chronologischen Reihenfolge auf das Toolbox-Journal gedruckt wurden. Wir haben sie zur besseren Unterscheidung und zum leichteren Nachschlagen in roter bzw. schwarzer Farbe hervorgehoben. Um die dynamischen vertikalen Schaltflächen zu erstellen, verwenden wir die folgende Logik.

         createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);

Hier beginnen wir damit, den Namen der Schaltfläche zu konstruieren, indem wir das Makro „Symb“ (definiert als „Symb“) mit dem Index „i“ verketten, der mit der Funktion IntegerToString in eine Zeichenkette umgewandelt wird, um sicherzustellen, dass jede Schaltfläche eine eindeutige Kennung wie „Symb 0“, „Symb 1“ usw. hat. Wir setzen die Beschriftung der Schaltfläche auf den Namen des Handelssymbols bei Index „i“, der mit der Funktion SymbolName ermittelt wird, die den Namen des Symbols abruft, wobei der Parameter true sicherstellt, dass der Name in der Marktbeobachtungsliste enthalten ist. Wir setzen die x-Koordinate der Schaltfläche auf 600 Pixel und richten alle Symbolschaltflächen vertikal aus. Die y-Koordinate wird dynamisch berechnet als (50 + „YS1") + „i" multipliziert mit „YS1", wodurch jede Schaltfläche nacheinander nach unten positioniert wird, indem die Höhe der Schaltfläche („YS1", d. h. 25 Pixel) multipliziert mit dem Index „i" zum anfänglichen Offset von 50 Pixeln plus „YS1" addiert wird. Wir geben die Abmessungen der Schaltfläche mit den Werten „XS1" (90 Pixel für die Breite) und „YS1" (25 Pixel für die Höhe) an. Wir setzen die Textfarbe auf „clrW" (weiß), die Hintergrundfarbe auf eine graue Farbe, die Schriftgröße auf 11 und schließlich die Rahmenfarbe auf eine graue Farbe. Nach der Kompilierung ist das Ergebnis wie folgt:

SPALTE DER SYMBOL

Dies ist die Liste aller Symbole, die in der Marktbeobachtung enthalten sind. Wenn Sie die Symbole hinzufügen oder entfernen, werden sie automatisch erstellt, was uns darin bestärkt, eine dynamische Logik für ihre Erstellung zu verwenden. Entfernen wir einige der Paare, insbesondere die letzten 3, um zu sehen, ob dies der Fall ist.

WENIGER SYMBOLE

Sie können sehen, dass dies automatisch geschieht. Jetzt fügen wir die entfernten Symbole wieder hinzu und arbeiten weiter an einer Logik, die dabei hilft, das aktuell ausgewählte Symbol zu identifizieren und zu priorisieren und es vom Rest der Symbolschaltflächen zu unterscheiden.

      if (SymbolName(i, true) == _Symbol) {
         createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW);
      } else {
         createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);
      }

Anstatt die Schaltflächen mit ähnlichen Texturen zu erstellen, erstellen wir hier die Schaltflächen und differenzieren das Aussehen der Schaltfläche für das gerade aktive Symbol. Zunächst wird geprüft, ob der Symbolname bei Index „i“ mit dem aktuellen aktiven Symbol übereinstimmt, das über die vordefinierte Variable _Symbol abgerufen wird. Wenn es eine Übereinstimmung gibt, erstellen wir eine Schaltfläche mit einem bestimmten Aussehen, um das aktive Symbol hervorzuheben. Für das aktive Symbol setzen wir die Textfarbe auf „clrB" (schwarz), die Hintergrundfarbe auf lime, die Schriftgröße auf 11 und die Rahmenfarbe auf „clrW" (weiß). Dieses eindeutige Farbschema hebt das aktive Symbol zur einfachen Identifizierung hervor. Wenn das Symbol nicht mit dem aktiven Symbol übereinstimmt, übernimmt unsere Standardfunktion und erstellt die Schaltfläche mit dem Standardaussehensschema, das sicherstellt, dass inaktive Symbole in einer konsistenten und visuell vom aktiven Symbol verschiedenen Weise angezeigt werden. Nach dem Kompilieren haben wir Folgendes erreicht.

AUSGEWÄHLTES SYMBOL

Nachdem wir alle benötigten Symbolschaltflächen erstellt haben, fügen wir eine Fußzeile ein, die das Ende der Symbolvisualisierungsmatrix kennzeichnet, und fügen einige zusammenfassende Informationen hinzu. Der folgende Codeschnipsel wurde angepasst.

      // Create the base button for the RSI Dashboard at the end
      if (i == SymbolsTotal(true) - 1) {
         createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray);
      }

Um die Basisschaltfläche für das RSI-Dashboard zu erstellen und sicherzustellen, dass sie am Ende der Symbolliste positioniert ist, überprüfen wir zunächst, ob der aktuelle Index „i" gleich der Gesamtzahl der Symbole minus eins ist, was bedeutet, dass es das letzte Symbol in der Liste ist. Wenn diese Bedingung erfüllt ist, fahren wir mit der Erstellung der Basisschaltfläche fort. Wir verwenden die Funktion „createButton", um das Aussehen und die Position dieser Basisschaltfläche zu definieren. Um den Namen der Schaltfläche zu konstruieren, verketten wir die Zeichenfolge „Base" mit dem Index „i", um einen eindeutigen Bezeichner zu erhalten, und setzen die Bezeichnung auf „RSI Dashboard". Dies ist nur ein willkürlicher Wert, den Sie nach eigenem Ermessen ändern können. Dann positionieren wir die Schaltfläche an einer x-Koordinate von 600 Pixeln und einer y-Koordinate, die als (50 + „YS1") + (i * „YS1") + „YS1" berechnet wird, sodass sie direkt unter der letzten Symbolschaltfläche erscheint. Wir definieren die Breite der Schaltfläche als „XS1“ + „XS2“ multipliziert mit der Funktion ArraySize, die die Gesamtzahl der Elemente im Array „periods“ zurückgibt, um die Breite aller Schaltflächen des Zeitrahmens zusammen zu überbrücken, während wir die Höhe auf „YS1“ (25 Pixel) festlegen. Das Farbschema für die Schaltfläche entspricht dem Standardbild. Im Allgemeinen dient diese Basisschaltfläche als eindeutige Bezeichnung für das gesamte RSI-Dashboard und bildet einen visuellen Anker am unteren Ende der Symbolliste. Hier sind die Ergebnisse der Meilensteine.

BASIS DER SCHALTFLÄCHE

Schließlich müssen wir noch Schaltflächen erstellen, die RSI-Werte für jede Kombination aus Symbol und Zeitrahmen anzeigen. Der folgende Codeschnipsel wird dazu verwendet.

      // Loop to create buttons for RSI values for each symbol and timeframe
      for (int j = 0; j < ArraySize(periods); j++) {
         createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW);
      }
   }

Wir beginnen die Schleife mit einem ganzzahligen Zähler „j", der den Index des Zeitrahmens darstellt. Für jeden Zeitrahmen rufen wir die Funktion „createButton" auf, um eine Schaltfläche einzurichten, die den RSI-Wert für das aktuelle Symbol und den Zeitrahmen anzeigt. Wir generieren den Namen der Schaltfläche, indem wir die RSI-Zeichenkette mit dem Index „j" und dem Symbolnamen verknüpfen, um eine eindeutige Kennung für jede Schaltfläche zu gewährleisten. Wir setzen die Beschriftung der Schaltfläche auf „-/-", die wir später mit dem aktuellen RSI-Wert aktualisieren werden. Im Moment wollen wir nur sicherstellen, dass wir ein Symbol-Perioden-Raster-Layout erstellen können. Wir positionieren die Schaltfläche an einer x-Koordinate von (600 - „XS1") + (j * - „XS2"), wodurch die Schaltflächen horizontal mit einem Abstand von „XS2" (100 Pixel) angeordnet werden, wobei die Breite der Schaltfläche berücksichtigt wird. In ähnlicher Weise setzen wir die y-Koordinate auf (50 + „YS1") + (i * „YS1"), um sicherzustellen, dass die Schaltflächen vertikal auf der Grundlage des Symbolindexes platziert werden. Die Tastenabmessungen sind „XS2" - 1 für die Breite und „YS1" - 1 für die Höhe. Das Abziehen von 1 sorgt dafür, dass wir einen Rand von 1 Pixel lassen, sodass die Illusion eines Tastenrasters entsteht. Dann legen wir das Farbschema für die Schaltflächen mit einer Textfarbe von „clrB" (schwarz), einer Hintergrundfarbe von „clrW" (weiß), einer Schriftgröße von 12 und einer Rahmenfarbe von „clrW" (weiß) fest. Bei dieser Einstellung werden die RSI-Schaltflächen in einem Rasterlayout angeordnet, wobei jede mit einem bestimmten Symbol und Zeitrahmen verknüpft ist, was eine klare und strukturierte Ansicht der RSI-Werte über verschiedene Zeitrahmen hinweg bietet. Nach dem Kompilieren ergibt sich folgendes Bild:

INITIALISIERUNG DES SCHALTFLÄCHENRASTERS

Da wir nun das allgemeine Rasterlayout des Dashboards erstellen können, müssen wir nur noch die Indikatorwerte abrufen und die Schaltflächen aktualisieren. Bevor wir zu diesem Punkt kommen, können wir eine Vorschau der erstellten Objekte anzeigen, indem wir mit der rechten Maustaste auf eine beliebige Stelle des Charts klicken und die Option „Objektliste“ im Popup-Fenster auswählen. Alternativ können Sie auch „Strg + B“ drücken. Klicken Sie in dem Pop-up-Fenster auf „Alle“ („List all“), und die Liste der von uns erstellten Elemente wird angezeigt.

LISTE VON OBJEKTEN

Jetzt können wir sicher sein, dass wir die Objekte mit ihren jeweiligen eindeutigen Namen chronologisch im Chart anlegen. Damit ist sichergestellt, dass wir nichts unversucht lassen. Sie können sehen, dass z. B. bei den Symbolen das erste Symbol als „Symb 0“ bezeichnet wird, wodurch es sich von den anderen Symbolen unterscheidet. Hätten wir die Namen der Symbole nicht mit den jeweiligen Objekten verknüpft, hätten alle Schaltflächen denselben Symbolnamen, was zu einem Fehler bei der Erstellung der anderen Schaltflächen führen würde, da eine einmal erstellte Schaltfläche bestehen bleibt und keine andere Schaltfläche denselben Namen annehmen kann. Technisch gesehen würde also nur eine Schaltfläche erstellt und der Rest ignoriert werden. Wie raffiniert ist das denn? Okay, wir fahren nun fort, die Initialisierungswerte zu erstellen.

   // Loop to initialize RSI values and update buttons
   for(int i = 0; i < SymbolsTotal(true); i++) {
      for (int j = 0; j < ArraySize(periods); j++) {
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array

         ...

   }

Hier verwenden wir zwei Schleifen, um jedes Handelssymbol und jeden Zeitrahmen zu iterieren, um die RSI-Werte zu initialisieren und die entsprechenden Schaltflächen zu aktualisieren. Wir beginnen mit einer äußeren Schleife mit dem Index „i“, die ein Handelssymbol in der Marktbeobachtung auswählt, und innerhalb dieser Schleife haben wir eine innere for-Schleife, die jeden definierten Zeitrahmen für jedes ausgewählte Symbol durchläuft. Das bedeutet, dass wir zum Beispiel „AUDUSD“ auswählen und über alle definierten Zeitrahmen iterieren, d.h. „M1“, „M5“, „H1“, „H4“ und „D1“. Um die Logik zu verstehen, können wir uns einen Ausdruck ansehen.

   // Loop to initialize RSI values and update buttons
   for(int i = 0; i < SymbolsTotal(true); i++) {
      Print("SELECTED SYMBOL = ",SymbolName(i, true));
      for (int j = 0; j < ArraySize(periods); j++) {
         Print("PERIOD = ",EnumToString(periods[j]));

         ...

   }

Das ist es, was wir haben.

AUSDRUCK DER SYMBOLE

Als Nächstes erhalten wir für jede Kombination von Symbol und Zeitrahmen den RSI-Indikator-Handle, indem wir die Funktion iRSI mit den folgenden Parametern verwenden: Symbol, Zeitrahmen, RSI-Periode als 14 und Preistyp als die Schlusskurse. Das Handle „handleName_Id“ ist lediglich eine Ganzzahl, die irgendwo in unserem Computerspeicher definiert und gespeichert wird und uns die Interaktion mit den Daten des RSI-Indikators ermöglicht. Standardmäßig beginnt die Ganzzahl bei Index 10, und wenn ein weiterer Indikator erstellt wird, wird er um eins erhöht, also 10, 11, 12, 13, ... und so weiter. Zur Veranschaulichung drucken wir es aus.

         Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id);

Nach dem Kompilieren ergibt sich folgendes Bild:

HANDLES 1

Sie sehen, dass die ID der Handles bei 10 beginnt, und da wir 12 Symbole und 5 Perioden für jedes Symbol haben, erwarten wir insgesamt (12 * 5 = 60) 60 Indikator-Handles. Da wir aber mit der Indizierung bei 10 beginnen, müssen wir die ersten 9 Werte in das Ergebnis aufnehmen, um die endgültige Handle-ID zu erhalten. Mathematisch gesehen ist das 60 + 9, was zu (60 + 9 = 69) 69 führt. Lassen Sie uns anhand einer visuellen Darstellung überprüfen, ob dies korrekt ist.

HANDLES 2

Alles richtig. Anschließend wird das Array „rsi_Data_Val“ für die Verwendung als Zeitreihe festgelegt, indem die Funktion ArraySetAsSeries aufgerufen und das Flag auf true gesetzt wird, um die Aktion zu bestätigen. Mit dieser Konfiguration wird sichergestellt, dass die neuesten RSI-Werte am Anfang des Arrays stehen. Anschließend werden die RSI-Werte mit der Funktion CopyBuffer in das Array kopiert, wobei 0 den Pufferindex angibt, 0 die Startposition ist, 1 die Anzahl der abzurufenden Datenpunkte angibt und „rsi_Data_Val“ das Zielarray ist, in dem die abgerufenen Daten zur weiteren Analyse gespeichert werden. Lassen Sie uns die Daten in das Journal drucken und sehen, was wir erhalten. Wir verwenden die Funktion ArrayPrint, um das einfache dynamische Array zu drucken.

         ArrayPrint(rsi_Data_Val);

Im Folgenden finden Sie die Ergebnisse:

DATENFELD DRUCKEN

Das war großartig. Wir erhalten die richtigen Daten. Sie können die Daten im Indikatorfenster und im Datenfenster mit den Daten, die wir abrufen, vergleichen. Dies ist ein klares Zeichen dafür, dass wir die Daten erhalten haben und fortfahren können. Sie können sehen, wie schön es ist, alles zu bestätigen, was wir tun. Es wird empfohlen, bei jeder neuen Logik, die Sie dem Dashboard hinzufügen, einen Test zu kompilieren und durchzuführen, um sicherzustellen, dass Sie die erwarteten Ergebnisse erhalten. Jetzt brauchen wir nur noch die Indikatorwerte zu verwenden, um das Dashboard zu aktualisieren, und schon sind wir fertig.

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), clrBlack, clrW_Gray, clrWhite);

Hier verwenden wir eine nutzerdefinierte Funktion „update_Button“, um die Dashboard-Schaltflächen mit den entsprechenden Indikatordaten zu aktualisieren. Die Logik der Funktion ist wie folgt.

//+------------------------------------------------------------------+
//| Function to update a button                                      |
//+------------------------------------------------------------------+
bool update_Button(string objName, string text, color clrTxt = clrBlack,
                  color clrBG = clrWhite, color clrBorder = clrWhite
) {
   int found = ObjectFind(0, objName); // Find the button by name
   // Check if the button exists
   if (found < 0) {
      ResetLastError(); // Reset the last error code
      Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found
      return (false); // Return false if button is not found
   } else {
      // Update button properties
      ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
      ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color
      ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
      
      ChartRedraw(0); // Redraw the chart to reflect the updated button
      return (true); // Return true if update is successful
   }
}

Diese Funktion ist identisch mit der, die wir zum Erstellen einer Schaltfläche verwendet haben. Der Unterschied besteht darin, dass wir die Schaltfläche nicht erstellen, sondern sie mit der Funktion ObjectFind finden. Bei Erfolg wird die Nummer des Unterfensters (0 bedeutet das Hauptfenster des Charts) zurückgegeben, in dem das Objekt gefunden wurde. Wenn das Objekt nicht gefunden wird, gibt die Funktion eine negative Zahl zurück. Wenn also der Rückgabewert kleiner als 0 ist, bedeutet dies, dass es sich um eine negative Zahl handelt, die das Fehlen des Objekts anzeigt, und wir informieren über den Fehler, indem wir den Fehlercode ausgeben, und davor setzen wir den vorherigen möglichen Fehler zurück und geben false zurück, um die Funktion zu beenden. Bei einem Erfolg finden wir das Objekt und aktualisieren nur die Objekteigenschaften, d. h. den Text, die Farbschemata des Textes, den Hintergrund und den Rahmen. Nach dem Kompilieren ergibt sich folgendes Bild:

GEFÜLLTE DATEN

Ausgezeichnet. Wir haben jetzt die Indikatordaten im Raster. Sehen Sie sich an, wie korrekt die Daten mit nur einer einzigen Codezeile abgebildet werden. Bestätigen Sie auch, dass die Daten, die wir zuvor hatten, d.h. 55,27, für einen 1-Stunden-Zeitrahmen des aktuellen Symbols korrekt gefüllt sind. Um das Ganze noch ausgefallener zu gestalten, können wir Markteintrittsbedingungen auf der Grundlage von überkauften und überverkauften Niveaus definieren und die Farben für eine einfachere und nutzerfreundliche Referenzierung ändern. Anstatt feste Farben zu verwenden, sollten wir nun dynamische Farben einsetzen.

         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);

Zunächst deklarieren wir drei Variablen für die Schaltflächenfarben: „text_clr“ für die Textfarbe, „bg_clr“ für die Hintergrundfarbe und „border_clr“ für die Rahmenfarbe. Als Nächstes bestimmen wir die entsprechenden Farben auf der Grundlage des im RSI-Datenfeld gespeicherten RSI-Werts. Wenn der RSI-Wert unter 30 liegt, was in der Regel bedeutet, dass der Vermögenswert überverkauft ist, setzen wir die Textfarbe auf Weiß, die Hintergrundfarbe auf Grün und die Rahmenfarbe auf Grün, eine Kombination, die die Schaltfläche in Grün hervorhebt, um eine potenzielle Kaufgelegenheit zu signalisieren.

Wenn der RSI-Wert über 70 liegt, was darauf hindeutet, dass der Vermögenswert überkauft ist, setzen wir die Textfarbe auf Weiß, die Hintergrundfarbe auf Rot und die Rahmenfarbe auf Rot, um mit einer roten Schaltfläche auf eine potenzielle Verkaufsmöglichkeit hinzuweisen. Für RSI-Werte zwischen 30 und 70 wird das Standardfarbschema beibehalten, das einen Standard- oder neutralen Status widerspiegelt. Schließlich aktualisieren wir das Aussehen der Schaltfläche, indem wir die Funktion „update_Button“ aufrufen und die entsprechenden Parameter der Schaltfläche übergeben. Dadurch wird sichergestellt, dass jede Schaltfläche auf dem Dashboard den aktuellen RSI-Status genau wiedergibt und die Marktbedingungen visuell vermittelt. Zur Veranschaulichung des Meilensteins finden Sie hier eine visuelle Darstellung:

ENDGÜLTIGER ONINIT-MEILENSTEIN

Perfekt. Wir haben nun ein dynamisches und reaktionsschnelles Indikator-Dashboard erstellt, das die aktuell vorherrschenden Marktbedingungen auf dem Chart anzeigt und eine einfachere Referenzierung der Highlights ermöglicht. 

Der vollständige Quellcode, der für die Initialisierung des Indikator-Dashboards verantwortlich ist, ist nachstehend aufgeführt:

//+------------------------------------------------------------------+
//|                                       ADVANCED IND DASHBOARD.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

// Define button identifiers and properties
#define BTN1 "BTN1"
#define Desc "Desc "
#define Symb "Symb "
#define Base "Base "
#define RSI "RSI "
#define XS1 90
#define XS2 100
#define YS1 25
#define clrW clrWhite
#define clrB clrBlack
#define clrW_Gray C'230,230,230'

// Define the timeframes to be used
ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1};

// Function to truncate the ENUM_TIMEFRAMES string for display purposes
string truncPrds(ENUM_TIMEFRAMES period) {
   // Extract the timeframe abbreviation from the full ENUM string
   string prd = StringSubstr(EnumToString(period), 7);
   return prd; // Return the truncated string
}

// Global variables
int handleName_Id; // Variable to store the handle ID for the RSI indicator
double rsi_Data_Val[]; // Array to store the RSI values

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   // Create the main button for the pair with specific properties
   createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray);

   // Loop to create buttons for each timeframe with the corresponding RSI label
   for(int i = 0; i < ArraySize(periods); i++) {
      //Print("BEFORE: ",EnumToString(periods[i]));
      //Print("AFTER: ",truncPrds(periods[i]));
      createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray);
   }
   
   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(true); i++) {
      //Print("Index ",i,": Symbol = ",SymbolName(i,true));
      // Check if the symbol is the current symbol being traded
      if (SymbolName(i, true) == _Symbol) {
         createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW);
      } else {
         createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);
      }

      // Create the base button for the RSI Dashboard at the end
      if (i == SymbolsTotal(true) - 1) {
         createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray);
      }
      
      // Loop to create buttons for RSI values for each symbol and timeframe
      for (int j = 0; j < ArraySize(periods); j++) {
         createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW);
      }
   }
   
   // Loop to initialize RSI values and update buttons
   for(int i = 0; i < SymbolsTotal(true); i++) {
      //Print("SELECTED SYMBOL = ",SymbolName(i, true));
      for (int j = 0; j < ArraySize(periods); j++) {
         //Print("PERIOD = ",EnumToString(periods[j]));
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         //Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array
         //ArrayPrint(rsi_Data_Val);
         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
      }
   }
   
   return(INIT_SUCCEEDED); // Return initialization success
}

//+------------------------------------------------------------------+
//| Function to create a button                                      |
//+------------------------------------------------------------------+
bool createButton(string objName, string text, int xD, int yD, int xS, int yS,
   color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE,
   string font = "Arial Rounded MT Bold"
) {
   ResetLastError(); // Reset the last error code
   // Attempt to create the button
   if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
      Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails
      return (false); // Return false if creation fails
   }
   // Set button properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
   ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
   ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property
   ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected
   
   ChartRedraw(0); // Redraw the chart to reflect the new button
   return (true); // Return true if creation is successful
}

//+------------------------------------------------------------------+
//| Function to update a button                                      |
//+------------------------------------------------------------------+
bool update_Button(string objName, string text, color clrTxt = clrBlack,
                  color clrBG = clrWhite, color clrBorder = clrWhite
) {
   int found = ObjectFind(0, objName); // Find the button by name
   // Check if the button exists
   if (found < 0) {
      ResetLastError(); // Reset the last error code
      Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found
      return (false); // Return false if button is not found
   } else {
      // Update button properties
      ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
      ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color
      ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
      
      ChartRedraw(0); // Redraw the chart to reflect the updated button
      return (true); // Return true if update is successful
   }
}

Selbst wenn wir das Dashboard mit allen Elementen erstellt haben, müssen wir es bei jedem Tick aktualisieren, damit die Daten auf dem Dashboard die neuesten Daten widerspiegeln. Dies wird mit der Ereignisbehandlung von OnTick erreicht.

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

...

}

Diese Ereignisbehandlungsfunktion gibt keinen Wert zurück und sie wird immer dann aufgerufen, wenn sich die Preisnotierungen ändern. Um die Indikatorwerte zu aktualisieren, müssen wir hier einen Teil des Initialisierungscodes ausführen, damit wir die neuesten Werte erhalten. 

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   // Loop to update RSI values and buttons on each tick
   for(int i = 0; i < SymbolsTotal(true); i++) {
      for (int j = 0; j < ArraySize(periods); j++) {
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array
         
         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
      }
   }
}

Hier kopieren wir einfach den Code-Abschnitt und fügen ihn in den Initialisierungsabschnitt ein, der zwei „for“-Schleifen enthält, eine innere und eine äußere, und aktualisieren die Indikatorwerte. Dadurch wird sichergestellt, dass die Werte bei jedem Tick auf die zuletzt abgerufenen Daten aktualisiert werden. Auf diese Weise wird das Dashboard sehr dynamisch, lebendig und ansprechend in der Anwendung. Bei einem Graphics Interchange Format (GIF) ist dies der Meilenstein, den wir erreicht haben:

LIVE-UPDATES GIF

Schließlich müssen wir die von uns erstellten Objekte, d. h. das Indikator-Dashboard, loswerden, sobald der EA aus dem Chart entfernt ist. Dadurch wird sichergestellt, dass das Indikator-Dashboard zerstört wird und das Chart sauber bleibt. Daher ist diese Funktion für die Aufrechterhaltung eines sauberen und effizienten Handelsumfelds von entscheidender Bedeutung. Dies wird durch die Ereignisbehandlung von OnDeinit erreicht, die immer dann aufgerufen wird, wenn der Expert Advisor aus dem Chart entfernt wird.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   // remove all dashboard objects
   ObjectsDeleteAll(0,BTN1);
   ObjectsDeleteAll(0,Desc);
   ObjectsDeleteAll(0,Symb);
   ObjectsDeleteAll(0,Base);
   ObjectsDeleteAll(0,RSI);
   
   ChartRedraw(0);
}

Hier rufen wir die Funktion ObjectDeleteAll auf, um alle Objekte mit dem angegebenen Präfixnamen zu löschen. Die Funktion entfernt alle Objekte des angegebenen Typs, die Präfixe in Objektnamen verwenden. Die Logik ist hier ganz einfach. Wir rufen die Funktion für jedes der definierten Präfixe auf: “BTN1“, „Desc“, „Symb“, „Base“ und „RSI“, da dadurch alle mit diesen Präfixen verbundenen Objekte gelöscht werden und somit alle Schaltflächen und grafischen Elemente aus dem Chart entfernt werden. Abschließend rufen wir die Funktion ChartRedraw auf, um das Chart zu aktualisieren und die Entfernung dieser Objekte zu berücksichtigen, um sicherzustellen, dass das Chart aktualisiert und frei von durch das Programm erzeugten Restelementen ist. Hier ist, was wir haben:

ENTFERNUNG DES EXPERTEN

Jetzt haben wir ein voll funktionsfähiges Indikator-Dashboard in MQL5 erstellt. Der vollständige Quellcode, der für die Erstellung des Indikator-Dashboards verantwortlich ist, lautet wie folgt:

//+------------------------------------------------------------------+
//|                                       ADVANCED IND DASHBOARD.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

// Define button identifiers and properties
#define BTN1 "BTN1"
#define Desc "Desc "
#define Symb "Symb "
#define Base "Base "
#define RSI "RSI "
#define XS1 90
#define XS2 100
#define YS1 25
#define clrW clrWhite
#define clrB clrBlack
#define clrW_Gray C'230,230,230'

// Define the timeframes to be used
ENUM_TIMEFRAMES periods[] = {PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1};

// Function to truncate the ENUM_TIMEFRAMES string for display purposes
string truncPrds(ENUM_TIMEFRAMES period) {
   // Extract the timeframe abbreviation from the full ENUM string
   string prd = StringSubstr(EnumToString(period), 7);
   return prd; // Return the truncated string
}

// Global variables
int handleName_Id; // Variable to store the handle ID for the RSI indicator
double rsi_Data_Val[]; // Array to store the RSI values

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   // Create the main button for the pair with specific properties
   createButton(BTN1, "PAIR", 600, 50, XS1, YS1, clrW, clrGray, 15, clrGray);

   // Loop to create buttons for each timeframe with the corresponding RSI label
   for(int i = 0; i < ArraySize(periods); i++) {
      //Print("BEFORE: ",EnumToString(periods[i]));
      //Print("AFTER: ",truncPrds(periods[i]));
      createButton(Desc + IntegerToString(i), truncPrds(periods[i]) + " RSI 14", (600 - XS1) + i * -XS2, 50, XS2, YS1, clrW, clrGray, 13, clrGray);
   }
   
   // Loop to create buttons for each symbol
   for(int i = 0; i < SymbolsTotal(true); i++) {
      //Print("Index ",i,": Symbol = ",SymbolName(i,true));
      // Check if the symbol is the current symbol being traded
      if (SymbolName(i, true) == _Symbol) {
         createButton(Symb + IntegerToString(i), "*" + SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrB, clrLimeGreen, 11, clrW);
      } else {
         createButton(Symb + IntegerToString(i), SymbolName(i, true), 600, (50 + YS1) + i * YS1, XS1, YS1, clrW, clrGray, 11, clrGray);
      }

      // Create the base button for the RSI Dashboard at the end
      if (i == SymbolsTotal(true) - 1) {
         createButton(Base + IntegerToString(i), "RSI DashBoard", 600, (50 + YS1) + (i * YS1) + YS1, XS1 + XS2 * ArraySize(periods), YS1, clrW, clrGray, 11, clrGray);
      }
      
      // Loop to create buttons for RSI values for each symbol and timeframe
      for (int j = 0; j < ArraySize(periods); j++) {
         createButton(RSI + IntegerToString(j) + " " + SymbolName(i, true), "-/-", (600 - XS1) + (j * -XS2), (50 + YS1) + (i * YS1), XS2 - 1, YS1 - 1, clrB, clrW, 12, clrW);
      }
   }
   
   // Loop to initialize RSI values and update buttons
   for(int i = 0; i < SymbolsTotal(true); i++) {
      //Print("SELECTED SYMBOL = ",SymbolName(i, true));
      for (int j = 0; j < ArraySize(periods); j++) {
         //Print("PERIOD = ",EnumToString(periods[j]));
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         //Print("PERIOD = ",EnumToString(periods[j]),": HANDLE ID = ",handleName_Id);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array
         //ArrayPrint(rsi_Data_Val);
         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
      }
   }
   
   return(INIT_SUCCEEDED); // Return initialization success
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   // remove all dashboard objects
   ObjectsDeleteAll(0,BTN1);
   ObjectsDeleteAll(0,Desc);
   ObjectsDeleteAll(0,Symb);
   ObjectsDeleteAll(0,Base);
   ObjectsDeleteAll(0,RSI);
   
   ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   // Loop to update RSI values and buttons on each tick
   for(int i = 0; i < SymbolsTotal(true); i++) {
      for (int j = 0; j < ArraySize(periods); j++) {
         // Get the handle ID for the RSI indicator for the specific symbol and timeframe
         handleName_Id = iRSI(SymbolName(i, true), periods[j], 14, PRICE_CLOSE);
         ArraySetAsSeries(rsi_Data_Val, true); // Set the array to be used as a time series
         CopyBuffer(handleName_Id, 0, 0, 1, rsi_Data_Val); // Copy the RSI values into the array
         
         // Declare variables for button colors
         color text_clr, bg_clr, border_clr;
         
         // Determine button colors based on RSI value
         if (rsi_Data_Val[0] < 30) {
            text_clr = clrWhite; bg_clr = clrGreen; border_clr = clrGreen;
         } else if (rsi_Data_Val[0] > 70) {
            text_clr = clrWhite; bg_clr = clrRed; border_clr = clrRed;
         } else {
            text_clr = clrBlack; bg_clr = clrW_Gray; border_clr = clrWhite;
         }

         // Update the button with the RSI value and colors
         update_Button(RSI + IntegerToString(j) + " " + SymbolName(i, true), DoubleToString(rsi_Data_Val[0], 2), text_clr, bg_clr, border_clr);
      }
   }
}
//+------------------------------------------------------------------+
//| Function to create a button                                      |
//+------------------------------------------------------------------+
bool createButton(string objName, string text, int xD, int yD, int xS, int yS,
   color clrTxt, color clrBg, int fontSize = 12, color clrBorder = clrNONE,
   string font = "Arial Rounded MT Bold"
) {
   ResetLastError(); // Reset the last error code
   // Attempt to create the button
   if (!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {
      Print(__FUNCTION__, ": failed to create Btn: ERR Code: ", GetLastError()); // Print error message if button creation fails
      return (false); // Return false if creation fails
   }
   // Set button properties
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD); // Set X distance
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD); // Set Y distance
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS); // Set X size
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS); // Set Y size
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_RIGHT_UPPER); // Set corner position
   ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
   ObjectSetString(0, objName, OBJPROP_FONT, font); // Set font type
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontSize); // Set font size
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBg); // Set background color
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false); // Set background property
   ObjectSetInteger(0, objName, OBJPROP_STATE, false); // Set button state
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false); // Set if the button is selectable
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false); // Set if the button is selected
   
   ChartRedraw(0); // Redraw the chart to reflect the new button
   return (true); // Return true if creation is successful
}

//+------------------------------------------------------------------+
//| Function to update a button                                      |
//+------------------------------------------------------------------+
bool update_Button(string objName, string text, color clrTxt = clrBlack,
                  color clrBG = clrWhite, color clrBorder = clrWhite
) {
   int found = ObjectFind(0, objName); // Find the button by name
   // Check if the button exists
   if (found < 0) {
      ResetLastError(); // Reset the last error code
      Print("UNABLE TO FIND THE BTN: ERR Code: ", GetLastError()); // Print error message if button is not found
      return (false); // Return false if button is not found
   } else {
      // Update button properties
      ObjectSetString(0, objName, OBJPROP_TEXT, text); // Set button text
      ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt); // Set text color
      ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG); // Set background color
      ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder); // Set border color
      
      ChartRedraw(0); // Redraw the chart to reflect the updated button
      return (true); // Return true if update is successful
   }
}


Schlussbemerkung

Zusammenfassend lässt sich sagen, dass die Erstellung eines RSI-Indikator-Dashboards mit mehreren Symbolen und mehreren Perioden in MetaQuotes Language 5 (MQL5) den Händlern ein nützliches Instrument zur Marktanalyse bietet. Dieses Dashboard zeigt RSI-Werte in Echtzeit für verschiedene Symbole und Zeitrahmen an und hilft Händlern, schneller fundierte Entscheidungen zu treffen.

Die Erstellung des Dashboards umfasste die Einrichtung von Komponenten, die Erstellung von Schaltflächen und deren Aktualisierung auf der Grundlage von Echtzeitdaten mithilfe von MQL5-Funktionen. Das Endprodukt ist funktionell und nutzerfreundlich.

Dieser Artikel zeigt, wie MQL5 zur Erstellung praktischer Handelswerkzeuge verwendet werden kann. Händler können dieses Dashboard replizieren oder modifizieren, um es an ihre spezifischen Bedürfnisse anzupassen und sowohl halbautomatische als auch manuelle Handelsstrategien in einem sich entwickelnden Marktumfeld zu unterstützen.

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

MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 29): Fortsetzung zu Lernraten mit MLPs MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 29): Fortsetzung zu Lernraten mit MLPs
Zum Abschluss unserer Betrachtung der Empfindlichkeit der Lernrate für die Leistung von Expert Advisors untersuchen wir in erster Linie die adaptiven Lernraten. Diese Lernraten sollen für jeden Parameter in einer Schicht während des Trainingsprozesses angepasst werden, und so bewerten wir die potenziellen Vorteile gegenüber der erwarteten Leistungsgebühr.
Kausalanalyse von Zeitreihen mit Hilfe der Transferentropie Kausalanalyse von Zeitreihen mit Hilfe der Transferentropie
In diesem Artikel wird erörtert, wie die statistische Kausalität zur Ermittlung prädiktiver Variablen eingesetzt werden kann. Wir werden die Verbindung zwischen Kausalität und Transferentropie untersuchen und einen MQL5-Code zur Erkennung von direktionalen Informationsübertragungen zwischen zwei Variablen vorstellen.
Stimmungsanalyse auf Twitter mit Sockets Stimmungsanalyse auf Twitter mit Sockets
Dieser innovative Trading-Bot integriert MetaTrader 5 mit Python, um die Stimmungsanalyse sozialer Medien in Echtzeit für automatisierte Handelsentscheidungen zu nutzen. Durch die Analyse der Twitter-Stimmung in Bezug auf bestimmte Finanzinstrumente übersetzt der Bot Trends in den sozialen Medien in umsetzbare Handelssignale. Es nutzt eine Client-Server-Architektur mit Socket-Kommunikation, die eine nahtlose Interaktion zwischen den Handelsfunktionen von MT5 und der Datenverarbeitungsleistung von Python ermöglicht. Das System demonstriert das Potenzial der Kombination von quantitativer Finanzwirtschaft und natürlicher Sprachverarbeitung und bietet einen innovativen Ansatz für den algorithmischen Handel, der alternative Datenquellen nutzt. Der Bot ist vielversprechend, zeigt aber auch Bereiche auf, die in Zukunft noch verbessert werden müssen, z. B. fortschrittlichere Techniken der Stimmungsanalyse und verbesserte Risikomanagementstrategien.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 28): GANs überarbeitet mit einer Anleitung zu Lernraten MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 28): GANs überarbeitet mit einer Anleitung zu Lernraten
Die Lernrate ist eine Schrittgröße in Richtung eines Trainingsziels in den Trainingsprozessen vieler maschineller Lernalgorithmen. Wir untersuchen die Auswirkungen, die die vielen Zeitpläne und Formate auf die Leistung eines Generative Adversarial Network haben können, eine Art neuronales Netz, das wir in einem früheren Artikel untersucht haben.