English 日本語
preview
Meistern der Log-Einträge (Teil 7): Protokolle auf dem Chart anzeigen

Meistern der Log-Einträge (Teil 7): Protokolle auf dem Chart anzeigen

MetaTrader 5Beispiele |
151 0
joaopedrodev
joaopedrodev

Einführung

Es gibt Dinge, die wir in der Hitze der Entwicklung tun, die eigentlich gar nicht für einen Artikel gedacht waren. Etwas, das in diesem Moment passiert, ein Detail, das nur auftaucht, um einen lästigen Schmerz zu lösen. Und das hier ist genau so eine Sache. Ich dachte sogar: „Nein, das ist zu einfach, es lohnt sich nicht einmal, es mitzuteilen... .“ Aber die Wahrheit ist, dass es sich als so nützlich erwiesen hat und so viel mehr Spaß gemacht hat, als ich erwartet hatte, dass es ein Verbrechen wäre, es für sich zu behalten.

Wenn Sie bis hierher gekommen sind, kennen Sie wahrscheinlich schon Logify, eine komplette Bibliothek zur Verwaltung und Speicherung von Protokollen bei der Entwicklung von Expert Advisors (EAs) in MQL5. Ein Tool, das entwickelt wurde, um ein für alle Mal die Einschränkungen der nativen Logs von MetaTrader 5 zu beseitigen und den Entwicklern mehr Kontrolle, Organisation und Professionalität zu bieten.

Im ersten Artikel dieser Serie, Meistern der Log-Einträge (Teil 1): Grundlegende Konzepte und erste Schritte in MQL5 haben wir die ersten Schritte zum Aufbau dieser Bibliothek unternommen. Wir untersuchten die Grundlagen, diskutierten, warum das blinde Verlassen auf die Standardprotokolle des MetaTrader eine Einladung zum Chaos ist, und begannen mit der Entwicklung einer robusten, anpassbaren und skalierbaren Lösung.

Und genau in der Mitte dieses Prozesses stieß ich auf eine Idee, die, ehrlich gesagt, noch nicht einmal in der Roadmap enthalten war. Während ich die Bibliothek selbst nutzte, wurde mir mit der Zeit klar, wie unbequem es ist, im Terminal nach Protokollen zu suchen, die Registerkarte Experten zu öffnen, Meldungen inmitten von Rauschen zu filtern oder, schlimmer noch, einen kritischen Fehler zu übersehen, weil er mitten in der Ausführung vom Bildschirm verschwunden ist. Es ist der Klassiker: die Suche nach der Nadel im Heuhaufen... während der Heuhaufen brennt.

In diesem Moment wurde es mir klar: „Was wäre, wenn diese Protokolle dort wären, wo sie wirklich Sinn machen? Auf dem Chart, vor dem Gesicht des Händlers, dort, wo der Roboter lebt und atmet.“ Und sehen Sie, ich spreche nicht davon, verstreute Beschriftungen, blinkende Pfeile oder grafische Objekte zu zeichnen, die mehr verwirren als helfen. Ich spreche von etwas viel Eleganterem, Unauffälligerem und Funktionalerem: der Verwendung des guten alten Comment().

Ja, diese Funktion, die die meisten Leute feierlich ignorieren, nur zum Debuggen einer Variablen verwenden und sie dann löschen. Nun, mit ein wenig Kreativität lässt sie sich in eine saubere, lesbare, in Echtzeit aktualisierte und absurd nützliche Log-Konsole verwandeln.

Und damit es sich nicht wie ein Verkaufsgespräch anhört, schauen Sie sich das einfach mal in Aktion an:

Um ganz ehrlich zu sein, wollte ich diesen Artikel gar nicht schreiben. Er wurde als zusätzliches Hilfsmittel geboren, eher aus einer persönliche Laune. Aber es ist so gut geworden, so praktisch und befriedigend, dass es einfach keinen Sinn machte, es in meinem Repository zu verstecken. Wenn Sie einfache, intelligente Lösungen mögen, die ein Problem wirklich lösen, bleiben Sie hier. Nach dem heutigen Tag werden Sie Comment() nie wieder auf die gleiche Weise betrachten. Verwandeln wir Ihr Chart in eine Log-Konsole.


Erstellen eines neuen Handlers

Nachdem Sie nun den Zweck dieser eleganten Visualisierung von Protokollen in einem Chart verstanden haben, kommen wir nun zur Sache. Die Idee dabei ist, einen neuen spezifischen Handler innerhalb unserer Bibliothek Logify zu erstellen, der für die Erfassung der Protokolle und deren direkte Anzeige im Chart unter Verwendung der Funktion Comment() verantwortlich ist.

Im Handlers-Ordner der Bibliothek erstellen wir eine neue Datei namens LogifyHandlerComment.mqh. Hier befindet sich die gesamte Logik, die für die Umwandlung herkömmlicher Protokolle in eine dynamische Anzeige im Chart selbst verantwortlich ist, praktisch eine Konsole, die an Ihren Roboter angeschlossen ist. Am Ende dieses Schritts sollte die Struktur Ihrer Bibliotheksdateien wie folgt aufgebaut sein:

In dieser Datei wird eine neue Klasse namens CLogifyHandlerComment deklariert, die wie die anderen Logify-Handler von der Basisklasse CLogifyHandler erbt. Dadurch bleibt die Architektur konsistent, modular und hält sich an das Muster, das wir seit dem ersten Artikel aufgebaut haben.

Der erste Schritt besteht wie immer darin, die grundlegenden Eigenschaften des Handlers zu definieren: wer er ist und was er tut. Dies beginnt im Klassenkonstruktor, wo wir seinen Namen als „comment“ definieren, was genau der Bezeichner ist, der innerhalb der Bibliothek verwendet wird, um diese spezielle Art der Protokollausgabe zu aktivieren.

Hier ist das anfängliche Skelett unserer Klasse, mit allen grundlegenden Methoden bereits deklariert Emit(), Flush() und Close(), bereit, als Nächstes implementiert zu werden:

//+------------------------------------------------------------------+
//|                                         LogifyHandlerComment.mqh |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "joaopedrodev"
#property link      "https://www.mql5.com/en/users/joaopedrodev"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "LogifyHandler.mqh"
//+------------------------------------------------------------------+
//| class : CLogifyHandlerComment                                    |
//|                                                                  |
//| [PROPERTY]                                                       |
//| Name        : CLogifyHandlerComment                              |
//| Heritage    : CLogifyHandler                                     |
//| Description : Log handler, inserts data into chart comment.      |
//|                                                                  |
//+------------------------------------------------------------------+
class CLogifyHandlerComment : public CLogifyHandler
  {
public:
                     CLogifyHandlerComment(void);
                    ~CLogifyHandlerComment(void);
   
   virtual void      Emit(MqlLogifyModel &data);         // Processes a log message and sends it to the specified destination
   virtual void      Flush(void);                        // Clears or completes any pending operations
   virtual void      Close(void);                        // Closes the handler and releases any resources
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandlerComment::CLogifyHandlerComment(void)
  {
   m_name = "comment";
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandlerComment::~CLogifyHandlerComment(void)
  {
  }
//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Emit(MqlLogifyModel &data)
  {
  }
//+------------------------------------------------------------------+
//| Clears or completes any pending operations                       |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Flush(void)
  {
  }
//+------------------------------------------------------------------+
//| Closes the handler and releases any resources                    |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Close(void)
  {
  }
//+------------------------------------------------------------------+



Planung der Konfiguration: Größe, Rahmen und Titel

Bevor wir die Hauptfunktionen direkt implementieren, müssen wir eine grundlegende Frage in jedem System lösen, das Wert auf Flexibilität legt: Wie konfiguriere ich dieses Ding? Die gute Nachricht ist, dass die Optionen einfach sind und im Rahmen des Logify-Vorschlags absolut Sinn machen. Keine obskuren Konfigurationen oder Parameter, von denen niemand weiß, wofür sie stehen. Hier liegt der Schwerpunkt auf der visuellen Kontrolle und Organisation.

Unser visueller Log-Handler wird vier Hauptkonfigurationsparameter anbieten:

  • size - Legt fest, wie viele Protokollzeilen im Chart angezeigt werden sollen. Mit anderen Worten: Wie groß ist das sichtbare Nachrichtenfenster?
  • frame_style - Der Stil des Rahmens, der das Protokoll im Chart umgibt. Hier können Sie wählen zwischen: ohne (kein Rahmen, einfach und direkt), einfacher Rahmen oder doppelter Rahmen.
  • direction - Die Richtung, in der die Meldungen angezeigt werden: von oben nach unten oder von unten nach oben.
  • title - Der Titel, der am oberen Rand des Rahmens angezeigt wird. Sie können den Namen Ihres EA hinzufügen

Mit diesen Parametern im Hinterkopf erstellen wir eine Struktur namens MqlLogifyHandleCommentConfig. Sie kapselt all diese Konfigurationen und wird in unserer Klasse CLogifyHandlerComment verwendet. Hier ist das Herzstück dieser Konfiguration:

//+------------------------------------------------------------------+
//| ENUMS                                                            |
//+------------------------------------------------------------------+
enum ENUM_LOG_FRAME_STYLE
  {
   LOG_FRAME_STYLE_NONE = 0,           // No rotation
   LOG_FRAME_STYLE_SINGLE,             // Rotate based on date
   LOG_FRAME_STYLE_DOUBLE,             // Rotate based on file size
  };
enum ENUM_LOG_DIRECTION
  {
   LOG_DIRECTION_UP = 0,               // Up
   LOG_DIRECTION_DOWN,                 // Down
  };
//+------------------------------------------------------------------+
//| Struct: MqlLogifyHandleComment                                   |
//+------------------------------------------------------------------+
struct MqlLogifyHandleCommentConfig
  {
   int size;                           // Space in lines that it will occupy
   ENUM_LOG_FRAME_STYLE frame_style;   // Display grid
   ENUM_LOG_DIRECTION direction;       // Direction
   string title;                       // log title
   
   //--- Default constructor
   MqlLogifyHandleCommentConfig(void)
     {
      size = 20;
      frame_style = LOG_FRAME_STYLE_SINGLE;
      direction = LOG_DIRECTION_UP;
      title = "LOGIFY";
     }
   
   //--- Destructor
   ~MqlLogifyHandleCommentConfig(void)
     {
     }

   //--- Validate configuration
   bool ValidateConfig(string &error_message)
     {
      //--- Saves the return value
      bool is_valid = true;
      
      //--- Check if size is greater than 0
      if(size <= 0)
        {
         size = 20;
         error_message = "Size must be greater than 0.";
         is_valid = false;
        }
      
      //--- Check len
      if(StringLen(title) > 40)
        {
         error_message = "Title is too long for frame. Max 40 chars.";
         is_valid = false;
        }
      
      //--- No errors found
      return(is_valid);
     }
  };

Die Klasse CLogifyHandlerComment hat dann diese Konfiguration als private Eigenschaft ( m_config ). Außerdem werden zwei wichtige Methoden für die Arbeit mit der Konfiguration vorgestellt: SetConfig() zur Validierung der Einstellungen und GetConfig() zur Rückgabe der aktuellen Einstellungen.

class CLogifyHandlerComment : public CLogifyHandler
  {
private:
   
   MqlLogifyHandleCommentConfig m_config;
   
public:
                     CLogifyHandlerComment(void);
                    ~CLogifyHandlerComment(void);
   
   //--- Configuration management
   void              SetConfig(MqlLogifyHandleCommentConfig &config);
   MqlLogifyHandleCommentConfig GetConfig(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CLogifyHandlerComment::CLogifyHandlerComment(void)
  {
   m_name = "comment";
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CLogifyHandlerComment::~CLogifyHandlerComment(void)
  {
  }
//+------------------------------------------------------------------+
//| Set configuration                                                |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::SetConfig(MqlLogifyHandleCommentConfig &config)
  {
   m_config = config;
   
   //--- Validade config
   string err_msg = "";
   if(!m_config.ValidateConfig(err_msg))
     {
      Print("[ERROR] ["+TimeToString(TimeCurrent())+"] Log system error: "+err_msg);
     }
   
   //--- Resize
   ArrayResize(m_logs, m_config.size);
  }
//+------------------------------------------------------------------+
//| Get configuration                                                |
//+------------------------------------------------------------------+
MqlLogifyHandleCommentConfig CLogifyHandlerComment::GetConfig(void)
  {
   return(m_config);
  }
//+------------------------------------------------------------------+


Kaskadierende Verschiebung erstellen

Warum brauchen wir diese kaskadenartige Verschiebung überhaupt? Einfach. Wenn wir wollen, dass die Protokolle im Chart mit den neuesten Meldungen unten und den ältesten Meldungen oben erscheinen, bis sie verschwinden, brauchen wir eine Struktur, die sich wie eine dynamische Warteschlange verhält. Andernfalls würde jedes neue Protokoll das vorherige einfach überschreiben oder, schlimmer noch, sich auf unbestimmte Zeit ansammeln und Ihr Chart in ein Durcheinander verwandeln.

Stellen Sie sich das so vor: Der verfügbare Platz im Chart ist nicht unendlich. Visuell können Sie nur etwa 10 Zeilen Text unterbringen, bevor er sich überschneidet oder vom Bildschirm verschwindet. Wir müssen also dafür sorgen, dass jede neue Nachricht den ersten Platz in der Warteschlange einnimmt und die anderen nach oben schiebt. Wenn die Grenze bereits erreicht ist (z. B. 10 Zeilen), wird die älteste Zeile einfach verworfen.

Dieses Verhalten ist klassisch in verschiedenen Anwendungen und in der Informatik als zirkulärer Puffer, gleitende Warteschlange oder, technisch gesehen, sich bewegende Elemente in einem linearen Array bekannt. Der Einfachheit halber sprechen wir hier von einer Kaskadenbewegung , denn die Informationen fließen tatsächlich wie Wasser, Zeile für Zeile.

Wie funktioniert das in der Praxis? Die Logik ist erstaunlich einfach, aber äußerst effizient:

  1. Wir verwalten ein Array namens m_logs[], das als unsere „visuelle Konsole“ fungiert. Dieses Array hat eine feste Größe, z. B. 10 Elemente, d. h. 10 Linien im Chart.
  2. Jedes Mal, wenn eine neue Protokollmeldung eintrifft, muss sie am Anfang der Liste, an Position 0 des Arrays, erscheinen.
  3. Dazu werden die vorhandenen Elemente verschoben: Die Elemente an Position 8 werden zu 9, die an Position 7 zu 8 und so weiter, bis die Elemente an Position 0 zu 1 verschoben werden. 
  4. Sobald dies geschehen ist, wird der Platz an Position 0 frei, und dort fügen wir die neue, frische Nachricht ein, die am oberen Rand des Bildschirms leuchtet.

Optisch verhält sich dies wie ein Dominoeffekt. Mit jeder neuen Nachricht wird die vorherige um eine Position nach unten verschoben. Wenn der Platz nicht mehr ausreicht, verschwindet derjenige, der am Ende der Warteschlange stand, einfach vom Bildschirm und aus dem Speicher. Einfach, sauber und effizient.

Ohne diesen Mechanismus würde jedes neue Protokoll das vorhergehende direkt in Comment() überschreiben, oder aber wir würden unkontrolliert Zeilen anhäufen und damit das Chart verschmutzen. Keines der beiden Szenarien ist wünschenswert. Daher ist die Kaskadierung nicht nur eine Frage der Ästhetik, sondern eine funktionale Notwendigkeit, um sicherzustellen, dass unser Protokoll im Chart nützlich und lesbar ist.

Nachdem Sie nun genau wissen, wie die Kaskadierung funktioniert, wollen wir das Ganze mit der Methode Emit() in die Praxis umsetzen. Diese Methode wird immer dann aufgerufen, wenn eine neue Protokollmeldung verarbeitet und im Chart angezeigt werden muss. Und sie tut genau das, was wir besprochen haben: Sie wendet die Kaskadenverschiebung an, stellt den gesamten formatierten Text zusammen (einschließlich Rahmen, Titel und Ausrichtung) und gibt am Ende alles mit der systemeigenen Funktion Comment() in das Chart aus.

Um unübersichtlichen Code zu vermeiden, haben wir die Logik in Hilfsfunktionen aufgeteilt, die sich um die Zusammenstellung von Rahmen, Titeln und Zeilen kümmern.

Das Verfahren besteht aus drei einfachen Schritten:

  1. Nach Stufe filtern: Erreicht die Meldung nicht die im Handler konfigurierte Protokollstufe, wird sie verworfen.
  2. Kaskadenverschiebung: Verschiebt die vorhandenen Protokolle im Array m_logs[] um eine Position nach vorne, wodurch die Position Null für die neue Nachricht frei wird. (nach der Kaskadenverschiebung)
  3. Versammlung der Kommentare: Verwendet Hilfsfunktionen zur Erzeugung:
  • Header: Oberer Rahmen und Titel (falls vorhanden).
  • Body: Liste der Protokolle, geordnet nach der konfigurierten Richtung.
  • Footer: Schließen des Rahmens, falls konfiguriert.

Das Ergebnis wird mit MQL5s eigenem Comment() angezeigt. Verwenden Sie dazu einige Hilfsfunktionen, die da wären:

  • GetSideBorder() → Gibt das Zeichen für den Seitenrand zurück:
    • │ für einen einfachen Rahmen.
    • ║ für einen doppelten Rahmen.
    • "" (leer), wenn es keinen Rahmen gibt.
  • GetBorderTop() → Liefert die oberste Zeile des Rahmens:
    • Beispiel: ┌───────┐ oder ╔═══════╗
  • GetBorderMiddle() → Gibt das Trennzeichen unterhalb des Titels zurück:
    • Beispiel: ├───────┤ oder ╠═══════╣ 
  • GetBorderBottom() → Gibt die untere Zeile des Rahmens zurück:
    • Beispiel: └──────┘ oder ╚═══════╝ 
  • BuildHeader() → Stellt die Kopfzeile mit Titel (falls konfiguriert) und Rahmen zusammen.
  • BuildFooter() → Erzeugt nur die Fußzeile des Rahmens.
  • FormatLogLines() → Formatiert alle Protokollzeilen:
    • Erzeugt den seitlichen Rand an (falls vorhanden).
    • Berücksichtigt die Richtung ( LOG_DIRECTION_UP oder LOG_DIRECTION_DOWN ).

Dies ist schließlich der vollständige Code:

//+------------------------------------------------------------------+
//| Processes a log message and sends it to the specified destination|
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Emit(MqlLogifyModel &data)
  {
   //--- Check if log level is allowed
   if(data.level < this.GetLevel())
     {
      return;
     }

   //--- Shift logs to maintain history
   for(int i = m_config.size-1; i > 0; i--)
     {
      m_logs[i] = m_logs[i-1];
     }
   m_logs[0] = data;

   //--- Build the complete comment
   string comment = BuildHeader();
   comment += FormatLogLines();
   comment += BuildFooter();

   //--- Display on chart
   Comment(comment);
  }
//+------------------------------------------------------------------+
//| Returns the side border character based on frame style          |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetSideBorder()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE) return "│";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE) return "║";
   return "";
  }
//+------------------------------------------------------------------+
//| Returns the top border based on frame style                     |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetBorderTop()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE)
      return "┌───────────────────────────────────────────┐\n";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE)
      return "╔═══════════════════════════════════════════╗\n";
   return "";
  }
//+------------------------------------------------------------------+
//| Returns the middle separator based on frame style               |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetBorderMiddle()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE)
      return "├───────────────────────────────────────────┤\n";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE)
      return "╠═══════════════════════════════════════════╣\n";
   return "";
  }
//+------------------------------------------------------------------+
//| Returns the bottom border based on frame style                  |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::GetBorderBottom()
  {
   if(m_config.frame_style == LOG_FRAME_STYLE_SINGLE)
      return "└───────────────────────────────────────────┘\n";
   if(m_config.frame_style == LOG_FRAME_STYLE_DOUBLE)
      return "╚═══════════════════════════════════════════╝\n";
   return "";
  }
//+------------------------------------------------------------------+
//| Builds the comment header with optional title and frame         |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::BuildHeader()
  {
   string header = "";

   if(m_config.title != "" && m_config.title != NULL)
     {
      if(m_config.frame_style == LOG_FRAME_STYLE_NONE)
        {
         header += " " + m_config.title + "\n";
         header += "─────────────────────────────────────────────\n";
        }
      else
        {
         header += GetBorderTop();
         header += GetSideBorder() + " " + m_config.title + "\n";
         header += GetBorderMiddle();
        }
     }
   else
     {
      if(m_config.frame_style != LOG_FRAME_STYLE_NONE)
        {
         header += GetBorderTop();
        }
     }

   return header;
  }
//+------------------------------------------------------------------+
//| Builds the comment footer based on frame style                  |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::BuildFooter()
  {
   if(m_config.frame_style != LOG_FRAME_STYLE_NONE)
      return GetBorderBottom();
   return "";
  }
//+------------------------------------------------------------------+
//| Formats all log lines according to direction and frame          |
//+------------------------------------------------------------------+
string CLogifyHandlerComment::FormatLogLines()
  {
   string result = "";
   string side = GetSideBorder();

   if(m_config.direction == LOG_DIRECTION_UP)
     {
      for(int i = m_config.size-1; i >= 0; i--)
        {
         string line = m_logs[i].formated;
         if(line != "")
           {
            result += side + " " + line + "\n";
           }
         else
           {
            result += side + "\n";
           }
        }
     }
   else // LOG_DIRECTION_DOWN
     {
      for(int i = 0; i <= m_config.size-1; i++)
        {
         string line = m_logs[i].formated;
         if(line != "")
           {
            result += side + " " + line + "\n";
           }
         else
           {
            result += side + "\n";
           }
        }
     }

   return result;
  }
//+------------------------------------------------------------------+



Löschung des Protokolls am Ende

Um die Implementierung unserer CLogifyHandlerComment-Klasse abzuschließen, fehlt noch ein grundlegendes, aber äußerst notwendiges Detail: die Löschung dessen, was auf dem Chart gezeichnet wurde, wenn der Handler geschlossen wird. Denn wenn die Funktion Emit() für die Anzeige von Meldungen auf dem Bildschirm zuständig ist, hat die Methode Close() die entgegengesetzte Aufgabe: alles zu löschen.

Und wollen Sie wissen, was das Beste daran ist? Es ist lächerlich einfach. Im Gegensatz zu anderen Handlern, die z. B. Dateien oder Verbindungen schließen oder Speicher freigeben können, besteht unsere Aufgabe hier nur darin, den Kommentar aus dem Chart zu entfernen. Und MetaTrader tut dies ganz direkt mit der Comment()-Funktion selbst, ohne jegliche Argumente, also mit einem Comment("").

//+------------------------------------------------------------------+
//| Closes the handler and releases any resources                    |
//+------------------------------------------------------------------+
void CLogifyHandlerComment::Close(void)
  {
   //--- Clear
   Comment("");
  }
//+------------------------------------------------------------------+


Testen des CLogifyHandlerComment Handlers in der Praxis

Theorie ohne Praxis ist nur... Theorie. Starten wir also unseren Handler und sehen wir uns an, wie er sich im MetaTrader-Chart verhält. Um das zu testen, haben wir ein einfaches, unkompliziertes Skript erstellt. Hier haben wir ein typisches Szenario eines Expert Advisors simuliert, das verschiedene Ebenen von Protokollen auslöst: von Debug-Meldungen über Warnungen bis hin zu kritischen Fehlern.

Außerdem haben wir unseren Handler so konfiguriert, dass er die Protokolle direkt im Chart anzeigt, und zwar in einzelnen Zeilen, wobei die Protokolle Zeile für Zeile absteigend angezeigt werden, d. h. die neuesten Protokolle unten und die ältesten ganz oben.

//+------------------------------------------------------------------+
//| Import CLogify                                                   |
//+------------------------------------------------------------------+
#include <Logify/Logify.mqh>
CLogify logify;
//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Handler config
   MqlLogifyHandleCommentConfig m_config;
   m_config.size = 10;                                     // Max log lines
   m_config.frame_style = LOG_FRAME_STYLE_NONE;            // Frame style
   m_config.direction = LOG_DIRECTION_UP;                  // Log direction (up)
   m_config.title = "Expert name";                         // Log panel title
   
   //--- Create and configure handler
   CLogifyHandlerComment *handler_comment = new CLogifyHandlerComment();
   handler_comment.SetConfig(m_config);
   handler_comment.SetLevel(LOG_LEVEL_DEBUG);              // Min log level
   handler_comment.SetFormatter(new CLogifyFormatter("hh:mm:ss",
                                                      "{date_time} [{levelname}]: {msg}"));
   
   //--- Add handler to Logify
   logify.AddHandler(handler_comment);
   
   //--- Test logs
   logify.Debug("Initializing Expert Advisor...", "Init", "");
   Sleep(1500);
   logify.Debug("RSI indicator value calculated: 72.56", "Indicators", "Period: 14");
   Sleep(800);
   logify.Info("Buy order sent successfully", "Order Management", "Symbol: EURUSD, Volume: 0.1");
   Sleep(800);
   logify.Alert("Stop Loss adjusted to breakeven level", "Risk Management", "Order ID: 12345678");
   Sleep(500);
   logify.Error("Failed to send sell order", "Order Management", "Reason: Insufficient balance");
   Sleep(100);
   logify.Fatal("Failed to initialize EA: Invalid settings", "Initialization", "Missing or incorrect parameters");
   
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Warum Sleep() verwenden? Sleep() ist nicht zwingend erforderlich, sondern dient nur dazu, die Protokolle ein wenig zu strecken und zu simulieren, dass die Ereignisse zu unterschiedlichen Zeiten auftreten, was den Test realistischer und den visuellen Effekt auf das Chart angenehmer macht. Und wenn Sie der Meinung sind, dass der Text dem nicht gerecht wird, sehen Sie sich das Bild unten an, um zu sehen, wie es in der Praxis aussieht:

Schauen wir uns an, wie jeder der anderen verfügbaren Rahmen aussieht.


Damit haben wir unser CLogifyHandlerComment getestet, validiert und 100% Funktionsfähigkeit festgestellt.


Schlussfolgerung

Wir haben das Ende dieser Artikelserie über den Aufbau von Logify, einer vollständigen, robusten und vollständig anpassbaren Protokollbibliothek für MQL5, erreicht. Auf dieser Reise haben wir alles erforscht, von den Grundlagen der Protokollierungssysteme über die Strukturierung der Architektur, die Erstellung von Handlern und die Konfiguration von Formaten bis hin zu diesem visuellen Handler, der Protokolle direkt auf dem Chart mit der Funktion Comment() anzeigt.

Der Vorschlag war einfach, aber extrem notwendig: eine Lücke zu füllen, die in der Entwicklung von EAs und Tools in MetaTrader 5 existiert, das Fehlen eines anständigen, flexiblen und gut gestalteten Log-Systems. Diejenigen, die diese Serie verfolgen, haben nun eine Bibliothek in der Hand, die in der Lage ist, auf elegante und praktische Weise organisierte Protokolle zu erstellen, die nach Ebenen gefiltert, je nach Bedarf formatiert und sogar in Echtzeit auf dem Chart angezeigt werden.

Dieser Artikel markiert das Ende der Serie, zumindest vorläufig. Die Bibliothek ist funktional und vielseitig und wird den meisten Anforderungen mehr als gerecht. Die Technik ist jedoch ein lebendiger Organismus. Es gibt immer Raum für Verbesserungen, Anpassungen, Optimierungen oder sogar neue Ideen, die auf dem Weg entstehen. Und wenn dies geschieht, und das wird es höchstwahrscheinlich, können Sie sicher sein, dass ich diese Updates in neuen Artikeln bringen werde, um das Potenzial von Logify noch weiter auszubauen.

Im Moment erfüllt das Projekt seinen Zweck: Entwicklern ein leistungsfähiges Werkzeug zum Debuggen, Analysieren und Überwachen ihrer Roboter und Indikatoren an die Hand zu geben. Möge Logify Ihnen dabei helfen, das Chaos in Ordnung zu bringen und zufällige Nachrichten in nützliche Informationen zu verwandeln.

Wir sehen uns bei der nächsten Idee.


Dateiname
Beschreibung
Experts/Logify/LogiftTest.mq5
die Datei, in der wir die Funktionen der Bibliothek testen, mit einem praktischen Beispiel.
Include/Logify/Formatter/LogifyFormatter.mqh
die Klasse, die für die Formatierung von Protokolldatensätzen zuständig ist, indem sie Platzhalter durch bestimmte Werte ersetzt.
Include/Logify/Handlers/LogifyHandler.mqh
die Basisklasse für die Verwaltung von Log-Handlern, einschließlich der Einstellung des Levels und des Versands von Logs.
Include/Logify/Handlers/LogifyHandlerComment.mqh
Log-Handler, der formatierte Logs direkt an den Kommentar auf dem Terminal-Chart im MetaTrader sendet
Include/Logify/Handlers/LogifyHandlerConsole.mqh
der Handler der Logs, der formatierte Logs direkt an die Terminal-Konsole im MetaTrader sendet.
Include/Logify/Handlers/LogifyHandlerDatabase.mqh
der Log-Handler, der formatierte Logs an eine Datenbank sendet (derzeit enthält er nur einen Ausdruck, aber bald werden wir ihn in einer echten Sqlite-Datenbank speichern).
Include/Logify/Handlers/LogifyHandlerFile.mqh
der Log-Handler, der formatierte Logs in eine Datei sendet.
Include/Logify/Utils/IntervalWatcher.mqh
prüft, ob ein Zeitintervall verstrichen ist, sodass Sie Routinen in der Bibliothek erstellen können.
Include/Logify/Logify.mqh
die Kernklasse für die Protokollverwaltung, die Ebenen, Modelle und Formatierung integriert.
Include/Logify/LogifyLevel.mqh
die Datei, die die Log-Ebenen der Logify-Bibliothek definiert und eine detaillierte Kontrolle ermöglicht.
Include/Logify/LogifyModel.mqh die Struktur, die die Protokolleinträge modelliert, einschließlich Details wie Ebene, Nachricht, Zeitstempel und Kontext.


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

Beigefügte Dateien |
Logify.zip (21.43 KB)
MetaTrader 5 Machine Learning Blueprint (Teil 1): Datenlecks und Zeitstempelfehler MetaTrader 5 Machine Learning Blueprint (Teil 1): Datenlecks und Zeitstempelfehler
Bevor wir überhaupt damit beginnen können, ML für unseren Handel auf dem MetaTrader 5 zu nutzen, müssen wir uns mit einem der am meisten übersehenen Fallstricke befassen - dem Datenleck. In diesem Artikel wird erläutert, wie Datenlecks, insbesondere die Falle von MetaTrader 5-Zeitstempel, die Leistung unseres Modells verzerren und zu unzuverlässigen Handelssignalen führen können. Indem wir uns mit den Mechanismen dieses Problems befassen und Strategien zu seiner Vermeidung vorstellen, ebnen wir den Weg für den Aufbau robuster Modelle für maschinelles Lernen, die zuverlässige Vorhersagen in Live-Handelsumgebungen liefern.
Datenwissenschaft und ML (Teil 42): Forex-Zeitreihenvorhersage mit ARIMA in Python, alles was Sie wissen müssen Datenwissenschaft und ML (Teil 42): Forex-Zeitreihenvorhersage mit ARIMA in Python, alles was Sie wissen müssen
ARIMA, kurz für Auto Regressive Integrated Moving Average, ist ein leistungsfähiges traditionelles Zeitreihenprognosemodell. Mit der Fähigkeit, Spitzen und Schwankungen in Zeitreihendaten zu erkennen, kann dieses Modell genaue Vorhersagen über die nächsten Werte machen. In diesem Artikel werden wir verstehen, was es ist, wie es funktioniert, was Sie damit tun können, wenn es um die Vorhersage der nächsten Preise auf dem Markt mit hoher Genauigkeit und vieles mehr.
Entwicklung des Price Action Analysis Toolkit (Teil 28): Werkzeug für den Ausbruch aus der Eröffnungsspanne Entwicklung des Price Action Analysis Toolkit (Teil 28): Werkzeug für den Ausbruch aus der Eröffnungsspanne
Zu Beginn einer jeden Handelssitzung wird die Tendenz des Marktes oft erst deutlich, wenn die Kurse sich jenseits der Eröffnungsspanne bewegen. In diesem Artikel zeigen wir Ihnen, wie Sie einen MQL5 Expert Advisor erstellen, der automatisch Ausbrüche aus der Eröffnungsspanne erkennt und analysiert und Ihnen zeitnahe, datengestützte Signale für sichere Intraday-Einstiege liefert.
Einführung in MQL5 (Teil 17): Aufbau von Expert Advisors für eine Trendumkehr Einführung in MQL5 (Teil 17): Aufbau von Expert Advisors für eine Trendumkehr
Dieser Artikel zeigt Anfängern, wie man einen Expert Advisor (EA) in MQL5 erstellt, der auf Basis der Erkennung von Chart-Mustern mit Trendlinienausbrüchen und Umkehrungen handelt. Indem der Leser lernt, wie man Trendlinienwerte dynamisch abruft und mit der Preisaktion vergleicht, wird er in der Lage sein, EAs zu entwickeln, die in der Lage sind, Chart-Muster wie steigende und fallende Trendlinien, Kanäle, Keile, Dreiecke und mehr zu erkennen und zu handeln.