English 日本語
preview
Der MQL5 Standard Library Explorer (Teil 2): Verbinden mit Bibliothekskomponenten

Der MQL5 Standard Library Explorer (Teil 2): Verbinden mit Bibliothekskomponenten

MetaTrader 5Beispiele |
13 0
Clemence Benjamin
Clemence Benjamin

Inhalt

  1. Einführung
  2. Umsetzung
  3. Tests
  4. Schlussfolgerung


Einführung

Viele Entwickler, die in das MQL5-Ökosystem einsteigen – von enthusiastischen Neulingen bis hin zu erfahrenen Programmierern, die aus anderen Sprachen kommen – stoßen auf ein gemeinsames und frustrierendes Hindernis: Sie können Code schreiben, aber sie haben Schwierigkeiten, robuste, professionelle Expert Advisors zu erstellen. Die MQL5-Standardbibliothek ist ein wahres Kraftpaket an vorgefertigten, getesteten und optimierten Klassen, die dazu dienen, diesen Kampf zu vermeiden. Und doch kann die Fülle der Informationen überwältigend sein. Die eigentliche Herausforderung ist nicht der Mangel an Komponenten, sondern das Fehlen eines klaren Fahrplans – eines praktischen Leitfadens für die Integration dieser komplexen Bausteine in ein kohärentes, funktionierendes Handelssystem.

Entwickler haben oft einen Werkzeugkasten voller Präzisionsinstrumente, aber keinen Plan, wie sie diese zu einer vollständigen Lösung zusammensetzen können. Um diese Komplexität zu vereinfachen, verwenden wir eine Analogie aus dem wirklichen Leben: So wie der menschliche Körper aus spezialisierten Organen besteht, die unter der Koordination des Gehirns arbeiten, besteht ein Expert Advisor aus spezialisierten Modulen, die von seinem Hauptprogramm koordiniert werden.

In dieser Analogie fungiert der Expert Advisor (EA) als das Gehirn – der zentrale Entscheidungsträger. Unterstützende Bibliotheksklassen dienen als ihre Organe, die spezielle Funktionen ausführen:

  • Die Klasse CTrade führt Handelsgeschäfte aus – wie die Hände, die Aktionen ausführen.
  • Die Klasse CIndicators analysiert die Marktdaten wie die Augen die Umwelt wahrnehmen.
  • Die Klasse CAccountInfo überwacht den Zustand des Kontos – wie das Nervensystem, das Rückmeldungen sammelt und weiterleitet.

Abb. 1. Konzeptionelles Software-System

Abb. 2. Konzeptuelles biologisches System

Die Analogie ist hier nur ein konzeptioneller Aspekt, um die abstrakte Struktur der objektorientierte Programmierung (OOP) intuitiver zu machen und dem Leser zu verdeutlichen, wie die einzelnen Teile der Bibliothek zusammenwirken, um ein koordiniertes, effizientes Handelssystem zu bilden.

All dies sind Vorteile von OOP – Modularität, Wiederverwendbarkeit und Abstraktion – aber in dieser Serie überspringen wir absichtlich die tiefen theoretischen Erklärungen, um uns zuerst auf die praktische Anwendung zu konzentrieren. Viele Lernende verstehen Konzepte besser, wenn sie sie selbst ausprobieren, und wenn man erst einmal damit begonnen hat, Klassen praktisch zu erstellen und zu integrieren, werden die zugrunde liegenden OOP-Prinzipien auf natürliche Weise verständlich.

Für Leser, die es vorziehen, die OOP-Theorie zu erforschen, bevor sie fortfahren, gibt es viele ausgezeichnete Referenzen in der MQL5-Community und darüber hinaus, die Kapselung, Vererbung und Polymorphismus in der Tiefe erklären. Unser Ansatz ist jedoch praxisorientiert: durch reale Beispiele, Diagramme und Analogien, die Ihre Reise mit der MQL5-Standardbibliothek sowohl unterhaltsam als auch produktiv machen.

Am Ende dieser Diskussion werden wir über isolierte Beispiele hinausgehen, um einen allgemeine Blaupause zu enthüllen – den definitiven Leitfaden zum Verweben der leistungsstarken Fäden der MQL5-Standardbibliothek in das solide Gerüst eines professionellen Expert Advisors, während wir auch Möglichkeiten zur Erweiterung der Bibliothek selbst entdecken. 

Abb. 3. Der EA als Gehirn und die Klassen als Organe

Während der Expert Advisor zu Recht als das Gehirn oder Hauptprogramm behandelt wird, müssen wir auch verstehen, dass Module selbst durch die Einbeziehung anderer Module – die wir Abhängigkeiten nennen – aufgebaut werden können. Genauso wie wir lernen, ganze Bibliotheken in EA-Programme zu integrieren, brauchen wir das gleiche Fachwissen, wenn wir Module miteinander verflechten oder ein Modul in ein anderes implementieren.

Die obige Analogie zeigt, dass zwischen den Modulen zwei wichtige Beziehungen bestehen: im Allgemeinen durch die Koordination des Hauptprogramms, aber auch durch direkte Abhängigkeiten. Die Klasse CPanel steht beispielsweise nicht allein, sondern ist direkt von WndObj.mqh und ChartObjectsTxtControls.mqh abhängig.

Abb. 4. Kommunikation von Klasse zu Klasse

Die gute Nachricht ist, dass dieses Beziehungsnetz nicht kompliziert ist, wenn man es einmal verstanden hat. Bei der Verwendung eines beliebigen Moduls in einem EA, egal ob es sich um eine einfache Klasse oder ein komplexes abhängiges Modul handelt, gibt es konsistente, wiederholbare Schritte, die jedes Mal eine erfolgreiche Integration gewährleisten. Im folgenden Codefragmente sind die wichtigsten von Panel.mqh verwendeten Abhängigkeiten und die Stellen, an denen sie enthalten sind, hervorgehoben.

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>

Im nächsten Abschnitt werde ich die Schritte erläutern, die ich normalerweise befolge, wenn ich Bibliotheksklassen in meine Projekte integriere. Dies wird Ihnen helfen, ein solides Verständnis aufzubauen, bevor wir zur Umsetzungsphase übergehen, in der wir praktische Beispiele genauer untersuchen werden.

Integration einer beliebigen Klasse in einen Expert Advisor

In den meisten Fällen erfolgt die Auswahl der zu verwendenden Klassen nach der Definition der Gesamtstrategie oder des Ziels Ihres EAs – es besteht keine Notwendigkeit, Ihr Projekt mit unnötigen Komponenten zu überfrachten. In diesem Abschnitt liegt der Schwerpunkt jedoch nicht auf der Strategieentwicklung, sondern auf dem Verständnis des Integrationsprozesses selbst.

Unser Ziel ist es, das grundlegende Wissen und die praktischen Schritte zu vermitteln, die es Ihnen ermöglichen, jedes Bibliotheksmodul in Ihre zukünftigen Projekte zu integrieren, wann immer dies erforderlich ist. Der folgende Abschnitt beschreibt diese Schritte und veranschaulicht sie anhand eines Flussdiagramms.

Prozessablauf:

  1. Analysieren – Zweck der Klasse und Vererbungskette untersuchen
  2. Einbinden – #include-Anweisung mit korrektem Pfad hinzufügen
  3. Deklarationen – Objektinstanz in EA erstellen
  4. Initialisieren – Aufruf von Create() in OnInit() mit Parametern
  5. Laufzeit – Aktualisierung in OnTick(), Ereignisse behandeln
  6. Bereinigung – Freigabe von Ressourcen in OnDeinit()

Fehlerbehandlung:

  • Kompilierungsfehler → Abhängigkeiten prüfen
  • Erstellungsfehler → Überprüfung der Parameter
  • Laufzeitprobleme → Objektstatus überwachen

Abb. 5. Fahrplan für die Integration der MQL5-Bibliothek

Ablauf der Modul-zu-Modul-Integration

Abhängigkeitsanalyse

  • Eltern-Kind-Beziehungen: z. B. CPanel → WndObj → ChartObjectsTxtControls
  • Schnittstellen-Verträge: Welche Methoden müssen umgesetzt werden?
  • Datenfluss: Wie Informationen zwischen Modulen weitergegeben werden

Modul Vererbungshierarchie

Abb. 6. Modul-Vererbungshierarchie

Schritte zur Umsetzung

  1. Einbindungskette: Jedes Modul enthält seine direkten Abhängigkeiten
  2. Vererbung einrichten: Klassenhierarchien durch Erweiterungen
  3. Durchführung der Methode: Virtuelle Methoden bei Bedarf außer Kraft setzen
  4. Isolierte Prüfung: Module vor der EA-Integration unabhängig voneinander testen

Gemeinsame Muster

  • Mehrschichtige Architektur: Übergeordnete Module hängen von untergeordneten Modulen ab
  • Schnittstellentrennung: Module stellen saubere, zielgerichtete APIs zur Verfügung
  • Einfügen von Abhängigkeiten: Übergeben Sie benötigte Objekte, anstatt sie hart zu kodieren

Abb. 7. Ablauf der Modul-zu-Modul-Integration

Da unsere architektonische Grundlage fest etabliert und der Integrationsfahrplan klar definiert ist, treten wir nun in die Implementierungsphase ein, in der die Theorie zur Praxis wird. Hier wenden wir den strukturierten Integrationsprozess auf ein Arbeitsprojekt an und verwandeln das konzeptionelle Verständnis in einen funktionalen Expert Advisor.

Unser Implementierungsplan sieht vor, ein InfoPanel EA zu erstellen – ein Informations-Dashboard, das zeigt, wie die CPanel-Klasse und ihre Abhängigkeiten in einer Handelsumgebung zum Leben erweckt werden können. In diesem Beispiel wird genau gezeigt, wie die einzelnen Schritte unserer Integrationsmethodik angewendet werden, um ein sauberes, professionelles und erweiterbares System zu schaffen.


Umsetzung

Wenn Sie sich einer Bibliotheksklasse in MQL5 nähern, beginnt die Integration damit, dass Sie verstehen, was die Klasse wirklich ist – ihren Zweck, ihre Herkunft und wie sie in das Ökosystem der Chart-Steuerungen passt. CPanel ist mehr als nur ein rechteckiger Bereich; es ist eine in sich geschlossene Schnittstellenebene, die von CWndObj erbt und ihren eigenen Lebenszyklus im Chart verwaltet. Sie dient als architektonisches Gerüst für komplexe UI-Komponenten. Bevor ein Code in einem Expert Advisor erscheint, bildet dieses Verständnis den mentalen Rahmen für eine nahtlose Integration.

Analysieren der Klasse CPanel in Panel.mqh

Die erste Begegnung mit einer Klasse sollte immer investigativ sein. Die Lektüre der Kopfzeile Panel.mqh offenbart wichtige Beziehungen und Erweiterungspunkte. Die Klasse wird von CWndObj abgeleitet, was bedeutet, dass sie die Fähigkeit erbt, Ereignisse zu behandeln und Chart-Fenster zu verwalten. Im Inneren kapselt es CChartObjectRectLabel, das grafische Rechteck, das dem Panel seinen sichtbaren Rahmen gibt.

Durch die Identifizierung der öffentlichen Einstiegspunkte – insbesondere Create() und BorderType() – finden Sie die Steuerelement-Handles, mit denen Ihr EA interagieren wird. Diese Inspektion ersetzt Versuch und Irrtum durch fundierte Planung.

//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CPanel                                                     |
//| Usage: control that is displayed by                              |
//|             the CChartObjectRectLabel object                     |
//+------------------------------------------------------------------+
class CPanel : public CWndObj
  {
private:
   CChartObjectRectLabel m_rectangle;       // chart object
   //--- parameters of the chart object
   ENUM_BORDER_TYPE  m_border;              // border type

public:
                     CPanel(void);
                    ~CPanel(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- parameters of the chart object
   ENUM_BORDER_TYPE  BorderType(void)       const { return(m_border);                                  }
   bool              BorderType(const ENUM_BORDER_TYPE type);

protected:
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_rectangle.Description(m_text));           }
   virtual bool      OnSetColorBackground(void)   { return(m_rectangle.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_rectangle.Color(m_color_border));         }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnChange(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPanel::CPanel(void) : m_border(BORDER_FLAT)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPanel::~CPanel(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CPanel::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_rectangle.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set border type                                                  |
//+------------------------------------------------------------------+
bool CPanel::BorderType(const ENUM_BORDER_TYPE type)
  {
//--- save new value of parameter
   m_border=type;
//--- set up the chart object
   return(m_rectangle.BorderType(type));
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CPanel::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_rectangle.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CPanel::OnShow(void)
  {
   return(m_rectangle.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CPanel::OnHide(void)
  {
   return(m_rectangle.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CPanel::OnMove(void)
  {
//--- position the chart object
   return(m_rectangle.X_Distance(m_rect.left) && m_rectangle.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CPanel::OnResize(void)
  {
//--- resize the chart object
   return(m_rectangle.X_Size(m_rect.Width()) && m_rectangle.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CPanel::OnChange(void)
  {
//--- set up the chart object
   return(CWndObj::OnChange() && m_rectangle.BorderType(m_border));
  }
//+------------------------------------------------------------------+
   

Jede dieser Zeilen wird später direkt in einen Initialisierungsschritt in Ihrem EA übersetzt.

Schritte zur Integration

Schritt 1. Einbindung des Headers und der Abhängigkeiten

Sobald die Absicht klar ist, ist der nächste Schritt das Einbinden. Der EA muss CPanel und seine Basisklassen durch entsprechende #include-Anweisungen sichtbar machen. Die Dateistruktur von MQL5 ist streng, was die Include-Pfade angeht. Überprüfen Sie also immer, ob Ihre Panel.mqh und die sie unterstützenden Header (WndObj.mqh, ChartObjectsTxtControls.mqh, usw.) innerhalb des Projekts oder des globalen Include-Verzeichnisses zugänglich sind.

#include <Controls\Panel.mqh>  // gives access to CPanel and its internals

Zu diesem Zeitpunkt „weiß“ der EA, dass die Panel-Klasse existiert – sie ist bereit für die Instanziierung.

Schritt 2. Deklaration des Objekts

Die Deklaration legt fest, wo sich das Panel innerhalb des Speicherbereichs des EA befindet. Die meisten Entwickler entscheiden sich dafür, CPanel als Zeiger auf globaler Ebene zu deklarieren, um sicherzustellen, dass es zwischen Ereignisaufrufen bestehen bleibt und beim Aufräumen sicher gelöscht werden kann.

CPanel *mainPanel = NULL;

Dadurch wird das Objekt in allen Ereignishandlern (OnInit, OnTick, OnDeinit usw.) zugänglich, was einen konsistenten Lebenszyklus gewährleistet.

Schritt 3. Initialisierung – das Panel zum Leben erwecken

Die Erstellung erfolgt in OnInit(), und hier trifft das Design des Headers auf die Ausführung. Die Methode Create() erfordert sechs Argumente: die Chart-ID, einen eindeutigen Namen, das Zielteilfenster und vier Koordinaten, die das Rechteck des Panels definieren. Vergewissern Sie sich immer, dass der Aufruf „true“ zurückgibt; das Ergebnis „false“ deutet oft auf ein Parameter- oder Abhängigkeitsproblem hin.

int OnInit()
{
   mainPanel = new CPanel();
   if(!mainPanel.Create(ChartID(), "InfoPanel", 0, 10, 10, 400, 120))
   {
      Print("Panel creation failed.");
      delete mainPanel;
      mainPanel = NULL;
      return(INIT_FAILED);
   }

   mainPanel.BorderType(BORDER_RAISED);
   mainPanel.Text("Information Panel");
   return(INIT_SUCCEEDED);
}

Dieser Ausschnitt zeigt eine minimale, aber vollständige Initialisierung. Das Panel ist nun visuell im Chart vorhanden und nimmt das angegebene Rechteck ein.

Schritt 4. Laufzeitmanagement

Das Panel selbst muss vielleicht nicht ständig aktualisiert werden, wenn es statisch ist, aber hier erstreckt sich Ihre Integrationslogik. Möglicherweise möchten Sie untergeordnete Steuerelemente wie Kennzeichnungen oder Schaltflächen einbetten oder den Inhalt des Bereichs regelmäßig aktualisieren.

Für dynamische Aktualisierungen bietet ein OnTimer()-Ereignis einen stabilen Aktualisierungsmechanismus, ohne die OnTick()-Schleife zu belasten.

void OnTimer()
{
   // Placeholder for runtime UI updates
}

Bei jeder Aktualisierung können die im Bedienfeld angezeigten Daten aktualisiert werden, ohne dass das Objekt neu gezeichnet werden muss.

Schritt 5. Bereinigung und Fehlerbehandlung

Jedes dynamisch erstellte Objekt muss zerstört werden, um Lecks zu vermeiden. In OnDeinit() rufen Sie delete auf und setzen den Zeiger auf NULL. Dies gewährleistet eine ordnungsgemäße Freigabe und verhindert verwaiste Chart-Objekte.

void OnDeinit(const int reason)
{
   if(mainPanel)
   {
      mainPanel.OnHide();
      delete mainPanel;
      mainPanel = NULL;
   }
}

Wenn die Kompilierung fehlschlägt, prüfen Sie fehlende Einbindungen oder undefinierte Referenzen. Erstellungsfehler beziehen sich in der Regel auf falsche Koordinaten oder Namenskonflikte, während Laufzeitflimmern oder Ereignisprobleme häufig auf überlappende Chart-Objekte zurückzuführen sind.

Analysieren des CChartObjectText in ChartObjectsTxtControls.mqh

In diesem Abschnitt konzentrieren wir uns auf die Schnittstellenschicht des Headers ChartObjectsTxtControls.mqh. Der Klarheit und Kürze halber haben wir nur den öffentlichen und den Top-Level-Teil der Klasse extrahiert – die Teile, die definieren, wie wir mit ihr interagieren. Der vollständige Quelltext ist viel länger, aber für unsere Analyse benötigen wir nur die öffentliche Schnittstelle, die die „Eingangstür“ der Klasse darstellt.

Diese selektive Sichtweise ist beabsichtigt. Das Wesen der objektorientierten Programmierung (OOP) ist die Abstraktion – wir müssen nicht wissen, wie die Interna funktionieren, solange wir die Schnittstelle verstehen und wissen, wie man sie richtig verwendet. Die Schnittstelle definiert die verfügbaren Methoden, ihre Parameter und ihr erwartetes Verhalten – das ist alles, was wir brauchen, um Integrationen effektiv zu erstellen.

Unser Ziel ist es, die Kopfzeile zu analysieren, um ihren Zweck zu verstehen und die Ansatzpunkte zu identifizieren – die wichtigsten Methoden, die wir von unserem Haupt-EA aus aufrufen werden, um den Text im Chart zu beschreiben oder zu manipulieren. In diesem Fall haben wir diese Kopfzeile nicht für den Aufbau des Panels verwendet (wie bei der CPanel-Abhängigkeit), sondern nur für die Textbeschreibung in unserem Expert Advisor.

Als es früher als Abhängigkeit verwendet wurde, mussten wir uns nicht viel um seine interne Logik kümmern – das übergeordnete CPanel übernahm das. In diesem Bildungskontext studieren wir sie jedoch bewusst, um zu verstehen, wie die einzelnen Klassen aufgebaut sind und wie man sie unabhängig voneinander nutzen kann.

Der folgende Auszug zeigt die Schnittstelle von CChartObjectText, die alle wesentlichen Methoden für die Arbeit mit Textobjekten direkt im Chart bereitstellt. Dazu gehören Create(), FontSize() und Anchor() – unsere primären Ansatzpunkte bei der Integration von Textelementen in ein Panel oder EA. Mit diesen Methoden können wir Pixelpositionen definieren, Schriftstile festlegen, die Ausrichtung steuern und die Sichtbarkeit verwalten – alles, was für die Erstellung der Textbeschreibungsebene unseres InfoPanels erforderlich ist.

//+------------------------------------------------------------------+
//|                                      ChartObjectsTxtControls.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
//| All text objects.                                                |
//+------------------------------------------------------------------+
#include "ChartObject.mqh"
//+------------------------------------------------------------------+
//| Class CChartObjectText.                                          |
//| Purpose: Class of the "Text" object of chart.                    |
//|          Derives from class CChartObject.                        |
//+------------------------------------------------------------------+
class CChartObjectText : public CChartObject
  {
public:
                     CChartObjectText(void);
                    ~CChartObjectText(void);
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,const datetime time,const double price);
   //--- method of identifying the object
   virtual int       Type(void) const override { return(OBJ_TEXT); }
   //--- methods of access to properties of the object
   double            Angle(void) const;
   bool              Angle(const double angle) const;
   string            Font(void) const;
   bool              Font(const string font) const;
   int               FontSize(void) const;
   bool              FontSize(const int size) const;
   ENUM_ANCHOR_POINT Anchor(void) const;
   bool              Anchor(const ENUM_ANCHOR_POINT anchor) const;
   //--- methods for working with files
   virtual bool      Save(const int file_handle) override;
   virtual bool      Load(const int file_handle) override;
  };

Schritte zur Integration von CChartObjectText in den EA

Schritt 1: Einbinden

Wir fügen die Datei ChartObjectsTxtControls.mqh ganz oben im EA ein, damit die Klassendefinitionen sichtbar sind. Wir verwenden denselben Pfad wie die Bibliothek.

Wenn Sie Panel.mqh bereits einbinden, wird diese Kopfzeile oft mit einbezogen, aber wenn Sie sie explizit in den EA aufnehmen, werden Absicht und Abhängigkeiten deutlich.

#include <ChartObjects\ChartObjectsTxtControls.mqh>  // provides CChartObjectLabel et al.

Schritt 2: Deklarationen

Wir deklarieren Kennzeichnungsobjekte dort, wo EA-Lebenszyklusfunktionen auf sie zugreifen können – im globalen Bereich. Wir verwenden Stack-Objekte für Chart-Textklassen (sie verwalten Anhänge intern) oder Zeiger, wenn Sie die Semantik „Neu/Löschen“ bevorzugen. Wir benennen die Kennzeichnungen sorgfältig (einmalig pro Chart). Wenn wir mehrere Kennzeichnungen erwarten, verwenden wir eindeutige Namensmuster: “InfoPanel_123_txt1“.

// global declarations
CChartObjectLabel txtDesc;    // pixel-based label (good for panel body)

Schritt 3. Initialisieren (OnInit)

Als Nächstes erstellen wir die Kennzeichnung innerhalb von OnInit() unter Verwendung der Signatur, die in der Kopfzeile von ChartObjectsTxtControls.mqh gezeigt wird: Create(long chart_id, const string name, const int window, const int X, const int Y). Wir positionieren es relativ zu Ihrem Bedienfeldrechteck, um das Layout konsistent zu halten.

Wenn die Kennzeichnung nicht erstellt werden kann, werden hilfreiche Informationen (Chart-ID, Name, Koordinaten) protokolliert und die Initialisierung abgebrochen.

int OnInit()
{
  string txt_name = panel_name + "_txtDesc";
  int left = PANEL_X1 + PADDING;
  int top  = PANEL_Y1 + PADDING + 18;

  if(!txtDesc.Create(ChartID(), txt_name, 0, left, top))
  {
    Print("txtDesc.Create failed");
    return(INIT_FAILED);
  }

  txtDesc.Description("Hello trader! I am InfoPanel EA");
  txtDesc.FontSize(11);
  // txtDesc.Anchor(ANCHOR_LEFT_UPPER); // if API available
  return(INIT_SUCCEEDED);
}

Schritt 4. Runtime (OnTick/OnTimer/OnChartEvent)

Der Aufwand zur Laufzeit für statischen Text soll minimal bleiben. Wenn wir dynamische Felder benötigen, aktualisieren wir Description() aus OnTimer() (setzen von EventSetTimer() in OnInit()), nicht bei jedem Tick. Wenn wir Interaktivität hinzufügen (Schaltflächen oder Bearbeitungen aus derselben Kopfzeile), leiten wir das an OnChartEvent() weiter und überprüfen sparam auf Objektklicks.

Für mehrere Zeilen verwenden wir entweder mehrere CChartObjectLabel-Instanzen, die vertikal gestapelt sind, oder wir erzeugen \n und prüfen, ob Description() Zeilenumbrüche akzeptiert; falls nicht, bevorzugen wir separate Kennzeichnungen.

void OnTimer()
{
  // e.g., update price line inside the panel
  txtDesc.Description("Bid: " + DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits));
}

Schritt 5: Aufräumen (OnDeinit)

Wir entfernen das Chart-Objekt, wenn der EA beendet wird. Wir verwenden die Methode Delete() der Klasse (oder ObjectDelete() als Ausweichlösung). Intern rufen wir keine geschützten Methoden anderer Klassen auf – wir löschen einfach unsere Kennzeichnungen und geben den Zeiger auf das Panel frei.

void OnDeinit(const int reason)
{
  txtDesc.Delete();        // removes the chart object
  // delete panel pointer here if allocated
  EventKillTimer();        // if used
}

Zusammenfügen der Teile zu einem EA

Die abschließende Integration der CPanel-Klasse in unser InfoPanel EA vervollständigt die grundlegende Phase unserer Studie zur Standardbibliothek. Dieses Panel ist kein interaktives Dashboard oder ein Trading Controller, sondern eine statische Informationsanzeige, eine einfache, aber professionelle Markierung auf dem Chart, die das verwendete Produkt offen beschreibt. Viele Entwickler könnten sich für ein ähnliches Design entscheiden, um die Identität und die Details ihres EA direkt im Chart anzuzeigen und so die Beschränkung durch den standardmäßigen Chart-Namen oder die Notwendigkeit, sich durch die Eigenschaften durchzuarbeiten, zu vermeiden. Sie dient als subtile, aber praktische Signatur – ein visuelles Zeichen, das Klarheit in die Umgebung bringt.

Auch wenn das Konzept bescheiden erscheinen mag, liegt seine Bedeutung in der Methode, nicht in der Größenordnung. Hier haben wir den gesamten Zyklus der Klassenintegration geübt: von der Einbindung des Standardbibliothek-Headers über die Erstellung und Anpassung einer CPanel-Instanz bis hin zur Verknüpfung mit Chart-Textelementen für eine sinnvolle Darstellung. Dieser Prozess spiegelt den Kern dessen wider, wie professionelle MQL5-Komponenten strukturiert und verwaltet werden. Es geht darum, den architektonischen Rhythmus – Einbinden, Deklarieren, Erstellen und Kontrollieren – zu verstehen, den Sie später für weitaus fortgeschrittenere Systeme wiederverwenden werden.

Dieses erste Projekt ist absichtlich einfach gehalten, weil es den Grundstein für eine Erweiterung legt. Sobald Sie sich mit CPanel vertraut gemacht haben, wird das Hinzufügen anderer Elemente wie CChartObjectLabel für eine umfangreichere Textausgabe oder sogar CPicture für das Branding zur Selbstverständlichkeit. Zusammen bilden diese statischen Elemente ein kleines, aber komplettes Ökosystem der Informationsvisualisierung, das im Laufe unserer Studie an Komplexität zunehmen wird.

Die umfassendere Vision besteht darin, den Verstand zu schulen, modular zu denken und die Standardbibliothek nicht als starres Gerüst, sondern als flexible Grundlage zu sehen, die unbegrenzt erweitert werden kann. Jede Klasse – CPanel, CChartObjectLabel, CPicture und andere – ist ein Baustein. Was heute als einfaches Anzeigefeld beginnt, kann sich später zu einem umfassenden Arbeitsbereich mit Datenvisualisierung, nutzerdefinierten Steuerelementen und adaptiven Layouts entwickeln.

Unsere Reise mit CPanel markiert daher mehr als die Erstellung eines statischen Informationspanels; sie ist der Beginn der Beherrschung der Logik und des handwerklichen Könnens, die erforderlich sind, um ganze Systeme von der Standardbibliothek aufwärts aufzubauen – Schritt für Schritt, Schicht für Schicht.

//+------------------------------------------------------------------+
//| InfoPanel_EA.mq5                                                 |
//| Minimal example: creating a CPanel and a label text showing      |
//| a short EA description with panel background & border. We will   |
//| also add a logo to explore CPicture.                             |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property version   "1.0"

#include <Controls/Panel.mqh>                        // Panel header (correct path)
#include <ChartObjects\ChartObjectsTxtControls.mqh>  // for CChartObjectLabel

//---- Globals --------------------------------------------------------
CPanel             *infoPanel = NULL;   // pointer to panel (allocate/free)
CChartObjectLabel   txtDesc;            // label text inside the panel

// layout (pixels)
int PANEL_X1 = 10;
int PANEL_Y1 = 30;
int PANEL_X2 = 300;   // widened to give text room
int PANEL_Y2 = 100;
int PADDING  = 10;

// short description that fits comfortably on one line
string EA_DESCRIPTION = "Hello trader! I am InfoPanel EA";

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
{
   // allocate the panel object
   infoPanel = new CPanel();
   if(infoPanel == NULL)
   {
      Print("InfoPanel: allocation failed");
      return(INIT_FAILED);
   }

   // unique panel name
   string panel_name = "InfoPanel_" + IntegerToString((int)ChartID());

   // create the panel on the current chart (subwindow 0 = main)
   if(!infoPanel.Create(ChartID(), panel_name, 0, PANEL_X1, PANEL_Y1, PANEL_X2, PANEL_Y2))
   {
      Print("InfoPanel: Create() failed");
      delete infoPanel; infoPanel = NULL;
      return(INIT_FAILED);
   }

   // appearance: border type, header text, background and border colors
   infoPanel.BorderType(BORDER_RAISED);
   infoPanel.Text("InfoPanel");

   // Set a visible background and border color.
   // Use standard color constants; change them if you prefer another palette.
   // If your library expects ARGB or different color encoding, adapt accordingly.
   infoPanel.ColorBackground(clrSilver); // panel background
   infoPanel.ColorBorder(clrBlack);      // panel border

   // compute child label position inside panel (some padding below header)
   int left = PANEL_X1 + PADDING;
   int top  = PANEL_Y1 + PADDING + 18; // leave header area

   // create a CChartObjectLabel as panel body (unique name)
   string txt_name = panel_name + "_txtDesc";
   if(!txtDesc.Create(ChartID(), txt_name, 0, left, top))
   {
      Print("InfoPanel: txtDesc.Create() failed");
      delete infoPanel; infoPanel = NULL;
      return(INIT_FAILED);
   }

   // set the short description and font size using the correct API
   txtDesc.Description(EA_DESCRIPTION);
   txtDesc.FontSize(11);

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // remove the label object
   txtDesc.Delete();

   // free the panel (do NOT call protected methods)
   if(infoPanel != NULL)
   {
      delete infoPanel;
      infoPanel = NULL;
   }

   // safe kill of timer if later added
   EventKillTimer();
}

//+------------------------------------------------------------------+
//| Expert tick function (kept intentionally light)                  |
//+------------------------------------------------------------------+
void OnTick()
{
   // static info panel — no per-tick UI updates
}

//+------------------------------------------------------------------+
//| Chart event handler (optional extension point)                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{        
// placeholder for future interactive controls
}

Abb. 8. Erste Tests

In einem weiteren Schritt fügen wir dem Panel ein BMP-Logo hinzu, um ihm eine klare Identität zu verleihen. Ich habe immer die Verwendung von Open-Source-Tools für die Bilderstellung bevorzugt. GIMP ist eine ausgezeichnete Wahl, da es den Export von Bildern im BMP-Format schnell und einfach macht. Natürlich können Sie auch jede andere Software verwenden, mit der Sie vertraut sind. Sobald Ihr Logo erstellt und im Ordner „Images“ Ihres Terminals gespeichert ist, können wir uns mit der Klasse CPicture beschäftigen und planen, wie wir sie in unser Panel integrieren können.

Unten sind zwei Bilder zu sehen. Abb. 9 zeigt, wo das Logo in unserem Panel positioniert werden soll, während Abb. 10 das Logo zeigt, das ich mit GIMP erstellt habe. Wenn Sie das Bild im BMP-Format exportieren, achten Sie darauf, dass Sie eine Farbtiefe von 24 Bit, eine einzelne Ebene und keinen Alphakanal verwenden. Diese Einstellungen funktionierten bei den Tests einwandfrei – andere Exportkonfigurationen führten dazu, dass die Bilder nicht korrekt auf dem Bedienfeld angezeigt wurden. Das hier gezeigte Logobild hat nicht das richtige BMP-Format und funktioniert daher nicht richtig. Ich habe eine PNG-Version verwendet, die für Veröffentlichungszwecke unterstützt wird. Das entsprechende Logo finden Sie am Ende des Artikels.

LOGO

Abb. 9. Platzbedarf des EA-Logos

BMP-Logo

Abb. 10. 56x59 Pixel großes Logo

Verstehen und Vorbereiten der CPicture-Klasse für die Integration

Die Klasse CPicture erweitert die Grundlage, die wir bereits mit CPanel und CChartObjectLabel gelegt haben. So wie wir mit dem Panel einen Container und mit dem Label einen Text erhalten haben, stellt diese Klasse eine einfache Möglichkeit vor, Bitmap-Bilder im Chart anzuzeigen – ideal für unsere nächste Verbesserung, bei der wir dem Informationspanel eine Logo-Miniaturansicht hinzufügen wollen.

Im Kern fungiert CPicture als kleiner Wrapper um das Chart-Objekt CChartObjectBmpLabel. Es wird von CWndObj abgeleitet, d.h. es folgt denselben Lebenszyklusmethoden (Create, OnShow, OnHide, etc.), die wir bereits kennen. Dieses Design stellt sicher, dass wir sie in unseren EA integrieren können, indem wir die gleichen vertrauten Schritte wie bei den vorherigen Klassen verwenden – zuordnen, erstellen, konfigurieren und bereinigen.

Bei näherer Betrachtung fallen einige wichtige Punkte auf. Der Haupteintrag für die Erstellung, Create(chart, name, subwin, x1, y1, x2, y2), definiert den Bereich des Objekts auf dem Chart in Pixelkoordinaten, genau wie CPanel. Nach der Erstellung können wir das eigentliche Bild über BmpName(name) zuweisen, das auf eine .bmp-Datei im Ordner Images des Terminals verweist. Die Methode Border(value) passt die sichtbare Breite oder die Dicke des Rahmens des Bildbereichs an. Für eine saubere Logodarstellung werden wir diesen Wert jedoch wahrscheinlich auf Null setzen. Intern aktualisieren diese beiden Methoden das zugrunde liegende Chart-Objekt durch den OnChange().

Unsere Ansatzpunkte für Integration

Für unsere Zwecke brauchen wir uns nur auf einige praktische Punkte zu konzentrieren:

  • Create() – initialisiert das Objekt im Chart unter Verwendung eines eindeutigen Namens und von Pixelkoordinaten.
  • BmpName() – weist die anzuzeigende Bitmap-Datei zu, z. B. unser EA-Logo.
  • Border() – legt bei Bedarf Werte für den Rahmen oder die Breite fest (optional für Logos).
  • Destructor oder delete – gibt den Speicher sicher frei, wenn der EA entfernt wird.
//+------------------------------------------------------------------+
//|                                                      Picture.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndObj.mqh"
#include <ChartObjects\ChartObjectsBmpControls.mqh>
//+------------------------------------------------------------------+
//| Class CPicture                                                   |
//| Note: image displayed by                                         |
//|             the CChartObjectBmpLabel object                      |
//+------------------------------------------------------------------+
class CPicture : public CWndObj
  {
private:
   CChartObjectBmpLabel m_picture;          // chart object
   //--- parameters of the chart object
   int               m_border;              // border width
   string            m_bmp_name;            // filename

public:
                     CPicture(void);
                    ~CPicture(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- parameters of the chart object
   int               Border(void) const { return(m_border); }
   bool              Border(const int value);
   string            BmpName(void) const { return(m_bmp_name); }
   bool              BmpName(const string name);

protected:
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnChange(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPicture::CPicture(void) : m_border(0),
                           m_bmp_name(NULL)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPicture::~CPicture(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CPicture::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_picture.Create(chart,name,subwin,x1,y1))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set border width                                                 |
//+------------------------------------------------------------------+
bool CPicture::Border(const int value)
  {
//--- save new value of parameter
   m_border=value;
//--- set up the chart object
   return(m_picture.Width(value));
  }
//+------------------------------------------------------------------+
//| Set image                                                        |
//+------------------------------------------------------------------+
bool CPicture::BmpName(const string name)
  {
//--- save new value of parameter
   m_bmp_name=name;
//--- set up the chart object
   return(m_picture.BmpFileOn(name));
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CPicture::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_picture.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CPicture::OnShow(void)
  {
   return(m_picture.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CPicture::OnHide(void)
  {
   return(m_picture.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CPicture::OnMove(void)
  {
//--- position the chart object
   return(m_picture.X_Distance(m_rect.left) && m_picture.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CPicture::OnChange(void)
  {
//--- set up the chart object
   return(m_picture.Width(m_border) && m_picture.BmpFileOn(m_bmp_name));
  }
//+------------------------------------------------------------------+

Mit diesen einfachen Funktionen haben wir alles, was wir brauchen, um eine Bildkomponente in unser Panel zu integrieren.

Planung der Integration

Da wir nun wissen, wie CPicture funktioniert, können wir planen, wie wir es in unser InfoPanel_EA einbringen können. Die Bilddatei – unser Logo – sollte zunächst im BMP-Format vorbereitet werden. Wie bereits erwähnt, machen Tools wie GIMP den Export nach BMP einfach. Sobald wir die Datei haben, legen wir sie im Images-Ordner des Terminals ab (MQL5/Images/), damit der EA leicht über den Namen darauf zugreifen kann.

In unserem Code, werden wir:

  1. Die Kopfzeile Picture.mqh einfügen.
  2. Einen globalen Zeiger für unsere CPicture-Instanz deklarieren, ähnlich wie wir es für das Panel getan haben.
  3. Das Objekt in OnInit() erstellen und es innerhalb oder direkt über dem Panel positionieren.
  4. Den Bildnamen mit BmpName(„logo.bmp“) festlegen, um die von uns ausgewählte Datei zu laden.
  5. Die Ressourcen in OnDeinit() freigeben, um ein sauberes Beenden zu gewährleisten.

Auf diese Weise entwickelt sich unser Panel von einer einfachen Textanzeige zu einem Informationsbereich mit Markenzeichen – ein subtiler, aber sinnvoller Schritt, der die Präsentation des EA professioneller und erkennbarer macht.

Begründung für diesen Schritt

Obwohl dieser Zusatz unbedeutend erscheinen mag, vertieft er unser Verständnis dafür, wie jede visuelle Klasse aus der Standardbibliothek in der Praxis kombiniert werden kann. Jede Integration unterstreicht ein Muster: Erstellen, Konfigurieren und sicheres Verwalten des Lebenszyklus. Wenn wir die Logo-Integration abgeschlossen haben, werden wir drei verschiedene Standardklassen – CPanel, CChartObjectLabel und CPicture – zu einer einheitlichen Schnittstelle verbunden haben.

Dies bereitet uns auf den nächsten Abschnitt vor, in dem wir die vollständige Integration in Betrieb nehmen und das Logo von EA auf dem Panel selbst anbringen. Das wird unsere statische Informationstafel vervollständigen und als visuelle Überarbeitung von allem dienen, was wir bisher gelernt haben. Im nächsten Abschnitt wird gezeigt, wie diese Klassen harmonisch nebeneinander bestehen können, sodass unser EA sowohl informativ als auch visuell unverwechselbar ist.

//+------------------------------------------------------------------+
//| InfoPanel_EA.mq5                                                 |
//| Info panel with logo placed inside the panel and on foreground   |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property version   "1.0"

#include <Controls\Panel.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
#include <Controls\Picture.mqh>

//---- Globals --------------------------------------------------------
CPanel             *infoPanel = NULL;
CChartObjectLabel   txtDesc;
CPicture           *logo = NULL;

// layout (pixels)
int PANEL_X1 = 10;
int PANEL_Y1 = 30;
int PANEL_X2 = 300;
int PANEL_Y2 = 100;
int PADDING  = 10;

// logo size and filename
int LOGO_SIZE = 56;
string LOGO_FILE = "Logo.bmp";

// short description
string EA_DESCRIPTION = "Hello trader! I am InfoPanel EA";

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
{
   // 1. Create panel
   infoPanel = new CPanel();
   if(infoPanel == NULL)
   {
      Print("InfoPanel: allocation failed");
      return(INIT_FAILED);
   }

   string panel_name = "InfoPanel_" + IntegerToString((int)ChartID());

   if(!infoPanel.Create(ChartID(), panel_name, 0, PANEL_X1, PANEL_Y1, PANEL_X2, PANEL_Y2))
   {
      Print("InfoPanel: Create() failed");
      delete infoPanel; infoPanel = NULL;
      return(INIT_FAILED);
   }

   infoPanel.BorderType(BORDER_RAISED);
   infoPanel.Text("InfoPanel");
   infoPanel.ColorBackground(clrSilver);
   infoPanel.ColorBorder(clrBlack);

   // 2. Create label inside panel
   int label_left = PANEL_X1 + PADDING;
   int label_top = PANEL_Y1 + PADDING + 18;
   string txt_name = panel_name + "_txtDesc";
   if(!txtDesc.Create(ChartID(), txt_name, 0, label_left, label_top))
   {
      Print("InfoPanel: txtDesc.Create() failed");
      delete infoPanel; infoPanel = NULL;
      return(INIT_FAILED);
   }
   txtDesc.Description(EA_DESCRIPTION);
   txtDesc.FontSize(11);

   // 3. Set panel as background
   if(!ObjectSetInteger(ChartID(), panel_name, OBJPROP_BACK, true))
      PrintFormat("InfoPanel: unable to set OBJPROP_BACK for '%s' (non-fatal).", panel_name);

   // 4. Compute logo coordinates
   int logo_right = PANEL_X2 - PADDING;
   int logo_left = logo_right - LOGO_SIZE;
   int logo_top = PANEL_Y1 + PADDING;
   int logo_bottom = logo_top + LOGO_SIZE;
   string logo_name = panel_name + "_logo";

   // 5. Create logo
   logo = new CPicture();
   if(logo == NULL)
   {
      Print("InfoPanel: logo allocation failed (continuing without logo)");
   }
   else
   {
      if(!logo.Create(ChartID(), logo_name, 0, logo_left, logo_top, logo_right, logo_bottom))
      {
         Print("InfoPanel: logo.Create() failed (continuing without logo)");
         delete logo; logo = NULL;
      }
      else
      {
         // Ensure logo is in foreground
         if(!ObjectSetInteger(ChartID(), logo_name, OBJPROP_BACK, false))
            Print("InfoPanel: Failed to set OBJPROP_BACK=false for logo (non-fatal).");

         // Log coordinates
         PrintFormat("InfoPanel: Logo coords: left=%d, top=%d, right=%d, bottom=%d", logo_left, logo_top, logo_right, logo_bottom);

         // Load built-in logo.bmp with correct relative path (no fullpath needed).You can use custom logo names as long you update the code.
         string bmp_path = "\\Images\\Logo.bmp";
         bool ok = logo.BmpName(bmp_path);
         PrintFormat("InfoPanel: logo.BmpName('%s') returned %s", bmp_path, ok ? "true" : "false");
         if(!ok)
         {
            Print("InfoPanel: LOGO load failed. Verify MT5 installation has MQL5\\Images\\dollar.bmp.");
         }
         else
         {
            Print("InfoPanel: Logo loaded successfully!");
         }

         logo.Border(LOGO_SIZE);
         ChartRedraw(ChartID()); // Force redraw
      }
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(logo != NULL) { delete logo; logo = NULL; }
   txtDesc.Delete();
   if(infoPanel != NULL) { delete infoPanel; infoPanel = NULL; }
   EventKillTimer();
}

//+------------------------------------------------------------------+
//| OnTick                                                          |
//+------------------------------------------------------------------+
void OnTick()
{
}

//+------------------------------------------------------------------+
//| Chart events                                                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
}

Indem wir das CPicture-Logo innerhalb des CPanel-Rechtecks platziert und in den Vordergrund geholt haben, haben wir eine kleine, aber wirkungsvolle Demonstration durchgeführt: die Kombination von drei separaten Standardbibliotheksklassen – CPanel, CChartObjectLabel und CPicture – zu einem kohärenten UI-Element. Wir haben zuerst das Panel erstellt, dann die beschreibende Kennzeichnung hinzugefügt und schließlich die genauen Pixelkoordinaten berechnet, um das Logo so einzufügen, dass es sauber auf der rechten Seite des Panels sitzt. Der Aufruf von ObjectSetInteger(..., OBJPROP_BACK, true) für das Panel und OBJPROP_BACK = false für das Logo gab uns die Kontrolle über die Zeichenreihenfolge, sodass das Bild über dem Hintergrund, aber hinter anderen potenziellen interaktiven Objekten erscheint. Wir haben auch ChartRedraw() verwendet, um sicherzustellen, dass das Terminal sofort nach dem Laden der Bitmap neu gezeichnet wird. Alle diese Schritte folgten demselben Integrationsrhythmus: einbinden, deklarieren, erstellen, konfigurieren, Rückgabewerte prüfen und bereinigen.

Aus dieser Übung ergaben sich mehrere konkrete Erkenntnisse über die Klassenintegration. Erstens sind die Koordinatenberechnung und die Benennungsdisziplin wichtig – berechnen Sie die Positionen relativ zum Rechteck des Panels und verwenden Sie eindeutige, vorhersehbare Objektnamen (wir haben panel_name + „_logo“ verwendet), um Kollisionen zu vermeiden und die Fehlersuche zu vereinfachen. Zweitens erfordern externe Ressourcen eine sorgfältige Handhabung: Wir haben das Bitmap in MQL5\Images gespeichert, seinen relativen Pfad („\Images\Logo.bmp“) an BmpName() übergeben und Erfolg/Fehler protokolliert, damit der EA belastbar bleibt, wenn die Datei fehlt. Drittens ist der Lebenszyklusvertrag jeder Klasse zu beachten: Wir haben Objekte mit new zugewiesen, jeden Create()- und BmpName()-Aufruf validiert und Objekte immer mit OnDeinit() gelöscht, um zu vermeiden, dass Chart-Objekte verloren gehen oder Speicherplatz verloren geht. Viertens: Layering und Redraw-Kontrolle sind praktische Fähigkeiten – OBJPROP_BACK und ChartRedraw() ermöglichen es uns, Sichtbarkeit und Aktualisierung deterministisch zu verwalten.

Schließlich unterstreicht diese Arbeit ein wiederholbares Muster, das für jede Klasse der Standardbibliothek gilt: Identifizieren Sie die öffentlichen Ansatzpunkte (erstellen, konfigurieren der Setter wie Border()/BmpName() oder Text()), integrieren Sie sie in den EA-Lebenszyklus (OnInit → Runtime → OnDeinit), validieren Sie jeden Schritt mit einer klaren Protokollierung und berechnen Sie die Geometrie relativ zu den vorhandenen Elementen, damit das Layout wartbar bleibt. Was wie eine winzige Funktion aussieht – ein Logo in einem Panel – ist in Wirklichkeit ein kompaktes Übungsfeld für die umfassenderen Fähigkeiten, die wir aufbauen: das Zusammensetzen kleiner, gut funktionierender Module zu größeren, professionellen Oberflächen. Von hier aus können wir das Panel problemlos erweitern (responsives Layout, zusätzliche statische Metadatenzeilen oder optionale nicht-interaktive Symbole), da wir wissen, dass jede beliebige Klasse aus der Standardbibliothek mit genau demselben disziplinierten Ansatz integriert werden kann.


Tests

Nachdem wir den Code in MetaEditor 5 kompiliert hatten, setzten wir ihn auf einem Chart ein, um unser kleines Informationspanel mit seiner Beschreibung und seinem Logo zu beobachten – wie in der folgenden Abbildung gezeigt. Dieses Panel und seine Komponenten können als visuelle Identität für einen Expert Advisor betrachtet werden. Dieselbe Idee lässt sich auch anwenden, um Ihren zukünftigen Produkten eine einzigartige und erkennbare Präsenz in der Chart zu verleihen. Im Moment können wir stolz darauf sein, dass wir das Konzept erfolgreich demonstriert und unser Verständnis des Integrationsprozesses vertieft haben.

Testen des InfoPanel_EA

Abb. 11. InfoPanel_EA Prüfung auf der Chart


Schlussfolgerung

Mit dieser Übung haben wir erfolgreich eine wiederholbare Routine für die Integration von Klassen der Standardbibliothek in einen Expert Advisor eingeführt. Von der Erstellung eines CPanels bis zum Hinzufügen eines beschreibenden Labels und eines Logos über CChartObjectLabel und CPicture haben wir untersucht, wie verschiedene UI-Komponenten kombiniert und gesteuert werden können, um ein ausgefeiltes und funktionales Ergebnis zu erzielen. Diese kleine, aber vollständige Demonstration spiegelt die gleiche architektonische Disziplin wider, die für größere, komplexere Systeme erforderlich ist.

Das Ziel war nicht nur die Erstellung eines statischen Informationspanels, sondern auch die Verinnerlichung eines Arbeitsmusters – einbinden, deklarieren, initialisieren, konfigurieren und bereinigen – das im gesamten MQL5-Klassen-Ökosystem gilt. Im weiteren Verlauf der MQL5 Standard Library Explorer Serie wird diese grundlegende Übung unsere zukünftige Arbeit mit fortgeschritteneren Steuerelementen und zusammengesetzten Modulen vereinfachen.

Während bestimmte Verfeinerungen und kreative Variationen offen bleiben, wurde die wesentliche Idee erreicht: zu verstehen, wie modulare Klassen interagieren und wie sich kleine wiederverwendbare Teile zu kompletten Anwendungen entwickeln können. Wiederholen Sie dieses Beispiel, experimentieren Sie mit eigenen Farben, alternativen Layouts oder neuen Klassen wie CButton, CDialog oder CCheckBox. Jede Iteration vertieft Ihr Gespür dafür, wie sich das Objektmodell von MQL5 verhält.

Denken Sie daran: Modularisierung ist der Schlüssel zum Fortschritt. Viele leistungsfähige Bausteine sind bereits in der Standardbibliothek vorhanden; unsere Aufgabe als Entwickler ist es, sie zu intelligenten Systemen zusammenzusetzen. Wenn Sie dieses Handwerk beherrschen wollen, sollten Sie sich Zeit für das Studium der objektorientierten Programmierung (OOP) nehmen – es wird Ihnen den nötigen Einblick geben, um Ihre eigenen skalierbaren Frameworks zu entwerfen, zu erweitern und zu pflegen.

Diese Übung mag oberflächlich betrachtet einfach erscheinen, aber sie ist ein wichtiger Pinselstrich auf Ihrem Weg zur professionellen MQL5-Entwicklung. Wir haben schon viel angesprochen, aber es gibt noch viel mehr zu tun. Die angehängte Datei enthält die vollständige Implementierung – Sie können sie gerne testen, verfeinern und Ihre Gedanken mitteilen. Lassen Sie uns diese Diskussion mit Ihren Kommentaren und kreativen Erweiterungen noch aufschlussreicher gestalten.


Dateiname Beschreibung
InfoPanel_EA.mq5 Zeigt, wie man mehrere Klassen der Standardbibliothek in einen Expert Advisor integriert. Der EA erstellt mit CPanel ein Informationsanzeigefeld auf dem Chart, fügt mit CChartObjectLabel eine beschreibende TextKennzeichnung hinzu und bettet mit der Klasse CPicture ein Logobild ein. Dieses Beispiel dient als Grundlage für das Verständnis des modularen UI-Aufbaus und der Klasseninteraktion in MQL5.
Logo.zip  Es enthält ein Logo im unterstützten BMP-Format, das Sie in das Verzeichnis Images des Terminals extrahieren können. Wir empfehlen Ihnen jedoch, Ihre eigene Version mit Open-Source-Tools wie GIMP anzupassen, um eine noch individuellere Entwicklung zu ermöglichen.

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

Beigefügte Dateien |
Logo.zip (1.55 KB)
Selbstoptimierende Expert Advisors in MQL5 (Teil 15): Identifizierung linearer Systeme Selbstoptimierende Expert Advisors in MQL5 (Teil 15): Identifizierung linearer Systeme
Es kann schwierig sein, Handelsstrategien zu verbessern, weil wir oft nicht ganz verstehen, was die Strategie falsch macht. In dieser Diskussion führen wir die lineare Systemidentifikation ein, ein Teilgebiet der Kontrolltheorie. Lineare Rückkopplungssysteme können aus Daten lernen, um die Fehler eines Systems zu erkennen und sein Verhalten auf die gewünschten Ergebnisse auszurichten. Auch wenn diese Methoden keine vollständig interpretierbaren Erklärungen liefern, sind sie doch weitaus wertvoller, als überhaupt kein Kontrollsystem zu haben. Lassen Sie uns die Identifizierung linearer Systeme untersuchen und beobachten, wie sie uns als algorithmische Händler helfen kann, die Kontrolle über unsere Handelsanwendungen zu behalten.
Statistische Arbitrage durch kointegrierte Aktien (Teil 5): Screening Statistische Arbitrage durch kointegrierte Aktien (Teil 5): Screening
In diesem Artikel wird ein Verfahren zum Screening von Vermögenswerten für eine statistische Arbitragestrategie durch kointegrierte Aktien vorgeschlagen. Das System beginnt mit der regulären Filterung nach wirtschaftlichen Faktoren, wie z. B. Vermögensbereich und Branche, und endet mit einer Liste von Kriterien für ein Scoring-System. Für jeden statistischen Test, der beim Screening verwendet wurde, wurde eine entsprechende Python-Klasse entwickelt: Pearson-Korrelation, Engle-Granger-Kointegration, Johansen-Kointegration und ADF/KPSS-Stationarität. Diese Python-Klassen werden zusammen mit einer persönlichen Anmerkung des Autors über den Einsatz von KI-Assistenten für die Softwareentwicklung bereitgestellt.
Statistische Arbitrage durch kointegrierte Aktien (Teil 6): Bewertungssystem Statistische Arbitrage durch kointegrierte Aktien (Teil 6): Bewertungssystem
In diesem Artikel schlagen wir ein Bewertungssystem für die Strategien der Rückkehr zum Mittelwert vor, das auf der statistischen Arbitrage von kointegrierten Aktien basiert. In dem Artikel werden Kriterien vorgeschlagen, die von der Liquidität und den Transaktionskosten bis zur Anzahl der Kointegrationsränge und der Zeit bis zur Umkehrung des Mittelwerts reichen, wobei die strategischen Kriterien der Datenhäufigkeit (Zeitrahmen) und des Rückblickzeitraums für die Kointegrationstests berücksichtigt werden, die vor der Bewertung der Rangfolge richtig bewertet werden. Die für die Reproduktion des Backtests erforderlichen Dateien werden zur Verfügung gestellt, und ihre Ergebnisse werden ebenfalls kommentiert.
Die Grenzen des maschinellen Lernens überwinden (Teil 5): Ein kurzer Überblick über die Kreuzvalidierung von Zeitreihen Die Grenzen des maschinellen Lernens überwinden (Teil 5): Ein kurzer Überblick über die Kreuzvalidierung von Zeitreihen
In dieser Artikelserie befassen wir uns mit den Herausforderungen, denen sich algorithmische Händler beim Einsatz von auf maschinellem Lernen basierenden Handelsstrategien stellen müssen. Einige Herausforderungen innerhalb unserer Gemeinschaft bleiben unsichtbar, weil sie ein tieferes technisches Verständnis erfordern. Die heutige Diskussion dient als Sprungbrett, um die blinden Flecken der Kreuzvalidierung beim maschinellen Lernen zu untersuchen. Obwohl dieser Schritt oft als Routine behandelt wird, kann er bei unvorsichtiger Handhabung leicht zu irreführenden oder suboptimalen Ergebnissen führen. In diesem Artikel wird kurz auf die Grundlagen der Zeitreihen-Kreuzvalidierung eingegangen, um einen tieferen Einblick in ihre versteckten Schwachstellen zu ermöglichen.