English 日本語
preview
Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (II): Modularisierung

Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (II): Modularisierung

MetaTrader 5Beispiele | 17 Juni 2025, 07:42
42 0
Clemence Benjamin
Clemence Benjamin

Inhalt:



Einführung

Diese Diskussion markiert einen Durchbruch bei der Erstellung eines wartbaren Administrator-Panel als Expert Advisor. Die im vorigen Artikel vorgestellte Codeorganisation hat unseren Hauptcode erheblich verbessert, und heute gehen wir noch einen Schritt weiter, indem wir wichtige Komponenten in externe Dateien modularisieren. Dieser Ansatz stellt sicher, dass künftige Aktualisierungen sich auf die Verbesserung einzelner Komponenten konzentrieren, ohne andere Teile des Codes zu beeinträchtigen.

Ein deutliches Beispiel dafür, warum dies von Vorteil ist, ergab sich, als ich das Kommunikationspanel verfeinern musste. Das Blättern durch eine große, monolithische Codebasis, um den relevanten Abschnitt zu finden, war überwältigend. Indem wir den Code in strukturierte Module aufteilen, vereinfachen wir die Navigation und machen Entwicklung und Wartung viel effizienter.

Wir lassen uns von gut strukturierten Projekten inspirieren, die bewährte Praktiken der Codeorganisation vorleben. Heute werden wir die Modularisierung umsetzen, indem wir nutzerdefinierte Klassen für wesentliche Funktionalitäten einführen, die unser Programm definieren. Nachstehend finden Sie eine vollständige Liste der potenziellen Module, die wir zu entwickeln beabsichtigen.

Modul-Datei Beschreibung
AdminHomeDialog.mqh Damit wird der zentrale Bereich des Handelsadministrator-Panels deklariert, der den Zugang zu anderen Dienstprogrammen innerhalb des Programms ermöglicht.
Aunthentication.mqh Dieses Modul verwaltet die Nutzerauthentifizierung, einschließlich Passwortüberprüfung und Zwei-Faktor-Authentifizierung.
ThemeManager.mqh Verantwortlich für die Verwaltung des Aussehens und des Stylings Ihres Admin-Panels.
Telegram.mqh Enthält Funktionen und Klassen für die Interaktion mit Telegram, in der Regel für das Senden von Nachrichten, Benachrichtigungen.
CommunicationsDialog.mqh Sie sind für die Nutzeroberfläche (UI) und die Interaktionen im Zusammenhang mit den Kommunikationsfunktionen in Ihrem Admin-Panel zuständig. 
AnalyticsDialog.mqh Zur Anzeige und Verwaltung von Analysedaten, wie z. B. Handelsstatistiken, Leistungskennzahlen oder visuelle Charts in einem Dialogfenster.
 TradeManagementDialog.mqh Diese übernimmt die Erstellung der Nutzeroberfläche für handelsbezogene Aufgaben, bei denen die Nutzer Geschäfte effizient ausführen und verwalten können.

Nachdem diese Dateien erfolgreich erstellt wurden, können sie in den Hauptcode eingefügt werden

#include <Telegram.mqh>
#include <Authentication.mqh>
#include <AdminHomeDialog.mqh>
#include  <AnalyticsDialog.mqh>
#include <TradeManagementDialog.mqh>
#include <CommunicationDialog.mqh>

Alle Deklarationen für die Panel-Komponenten werden in den Include-Dateien untergebracht, während der Hauptcode hauptsächlich Definitionen enthält. Da Definitionen in der Regel kleiner sind als Deklarationen, bleibt das Hauptprogramm bei diesem Ansatz übersichtlich, was die Lesbarkeit und Wartbarkeit verbessert.

Ich bin mir sicher, dass Sie sich bereits vorstellen können, wie sich unser Projekt mit diesen Neuerungen weiterentwickeln wird. Im nächsten Abschnitt wird die Modularisierung im Detail erläutert, gefolgt von ihrer Umsetzung in diesem Projekt.

Erklärungen und Implementierung

Beziehung zwischen Hauptcode und Header-Datei



Überblick über die Diskussion

Mit dem kurzen Überblick aus der obigen Einführung werden wir uns nun eingehender mit der Modularisierung befassen, bevor wir uns der Entwicklung und Implementierung unserer Code-Komponenten widmen. Jedes Modul wird gründlich erklärt, mit einer Aufschlüsselung der Funktionalität jeder Codezeile.

Schließlich werden wir alle Module in eine neue Hauptcodebasis für das Handelsadministrator-Panel integrieren und es im Wesentlichen von Grund auf mit verbesserter Struktur und Effizienz neu aufbauen.

Am Ende dieser Diskussion werden wir die folgenden Dateien entwickelt, integriert und getestet haben:

  • AdminHomeDialog.mqh
  • Authentication.mqh
  • Telegram.mqh

Modularisierung

In der MQL5-Programmierung bezieht sich die Modularisierung auf die Praxis, ein Programm in kleinere, unabhängige und wiederverwendbare Teile zu zerlegen, hauptsächlich durch die Verwendung von Klassen, Funktionen und Include-Dateien. Dieser Ansatz ermöglicht es Entwicklern, spezifische Funktionsweisen in Modulen oder Klassen zu kapseln, wie z. B. die Erstellung von UI-Komponenten oder Handelslogik, die je nach Bedarf in verschiedenen Teilen einer Anwendung oder sogar in mehreren Anwendungen eingebunden oder instanziiert werden können. Auf diese Weise wird der Code überschaubarer, leichter zu pflegen und weniger fehleranfällig, da sich Änderungen an einem Modul nicht zwangsläufig auf andere Module auswirken. Dies fördert die Wiederverwendung von Code, verbessert die Lesbarkeit und erleichtert die gemeinsame Entwicklung in der MetaTrader 5-Umgebung.

In diesem Zusammenhang haben wir die Teilkomponenten unseres neuen Programms bereits in der Einleitung skizziert. Darüber hinaus gibt es weitere Ressourcen, die man zu diesem Thema lesen kann, und ich bin in verschiedenen Artikeln auf verschiedene Ansätze zur Anwendung der Modularisierung gestoßen.

In den nächsten Schritten werde ich Sie durch die Entwicklung jedes Moduls im Detail führen, damit Sie ein klares Verständnis für die Implementierung und Integration erhalten.


Implementierung des Codes

Jetzt ist es an der Zeit, unser MQL5-Wissen anzuwenden, um die Schlüsselkomponenten des EAs mit unserem Handelsadministrator-Panel zu entwickeln. Die gute Nachricht ist, dass diese Dateien so konzipiert sind, dass sie leicht angepasst und in Ihre eigenen Projekte integriert werden können.

Hauptstruktur einer Header-Datei in MQL5:

Eine Header-Datei in MQL5, typischerweise mit der Erweiterung .mqh, dient als Ort zur Definition von Klassen, Konstanten, Aufzählungen und Funktionsprototypen, die in andere MQL5-Skripte oder Expert Advisors aufgenommen werden können. Hier ist die typische Struktur, die auf dem Code anderer eingebauter Header-Dateien basiert:

  1. Datei-Metadaten:  Enthält Copyright-Informationen, Links und Versionskontrolle.
  2. Die Anweisung include: Listet andere Header-Dateien oder Bibliotheken auf, von denen der aktuelle Header abhängt.
  3. Definitionen/Konstanten: Definiert Makros oder Konstanten, die innerhalb der Klasse oder von anderen Teilen des Codes für konsistente Werte verwendet werden.
  4. Deklaration der Klasse: Deklariert die Klasse mit ihrer Vererbung (falls vorhanden), privaten Mitgliedern, öffentlichen und geschützten Methoden.
  5. Ereignis-Zuordnung: Verwendet Makros, um zu definieren, wie Ereignisse auf Mitgliedsfunktionen für die ereignisgesteuerte Programmierung abgebildet werden.
  6. Implementierung von Methoden: Obwohl dies nicht unbedingt für alle Header-Dateien erforderlich ist, sind in diesem Fall Methodenimplementierungen enthalten, was bei kleineren Klassen, bei denen die Kapselung nicht kritisch ist, üblich ist. Aus Leistungsgründen kann die Einbeziehung der Implementierung in MQL5 den Overhead für Funktionsaufrufe verringern.
  7. Konstruktoren und die Destruktoren: Diese sind Teil der Klassendefinition und geben an, wie Objekte der Klasse erstellt und zerstört werden.

Auf der Grundlage der obigen Skizze finden Sie hier eine beispielhaft Codevorlage:

// Meta Data here on top.
#include   // Include other necessary libraries or headers
#include 

//+------------------------------------------------------------------+
//| Defines                                                          |
//+------------------------------------------------------------------+
#define CONSTANT_NAME1    (value1)  // Constants or macro definitions
#define CONSTANT_NAME2    (value2)

//+------------------------------------------------------------------+
//| Class CClass                                                     |
//| Usage: Description of class purpose                              |
//+------------------------------------------------------------------+
class CClass : public CParentClass  // Inherits from another class if needed
  {
private:
   // Private member variables
   CSomeControl   m_control;  // Example control member

public:
   CClass(void);              // Constructor
   ~CClass(void);             // Destructor
   virtual bool   Create(/* parameters */);  // Virtual method for polymorphism
   virtual bool   OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);

protected:
   // Protected methods or members
   bool           CreateSomeControl(void);
   void           SomeEventHandler(void);

  };
//+------------------------------------------------------------------+
//| Event Handling                                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CClass)
   ON_EVENT(SOME_EVENT,m_control,SomeEventHandler)
EVENT_MAP_END(CParentClass)

// Constructor implementation
CClass::CClass(void)
  {
  }

// Destructor implementation
CClass::~CClass(void)
  {
  }

// Method implementations if included in the header
bool CClass::Create(/* parameters */)
  {
   // Implementation of create method
  }

// Event handler examples
void CClass::SomeEventHandler(void)
  {
   // Handle the event
  }

//+------------------------------------------------------------------+

Um Header-Dateien in MetaEditor zu erstellen, öffnen Sie eine neue Datei, indem Sie Strg + N drücken oder manuell durch das Menü navigieren. Wählen Sie im Pop-up-Fenster die Option Einschließen (*.mqh) und beginnen Sie mit der Bearbeitung. Die generierte Vorlage enthält standardmäßig einige Kommentare als Hinweise, die Ihnen helfen sollen.

Siehe dazu die folgende Abbildung:

Erstellen einer Include-Datei

Erstellen einer neue Header-Datei in MetaEditor

Hier ist die Standardvorlage für die Header-Datei, die einige kommentierte Hinweise zur Orientierung enthält.

//+------------------------------------------------------------------+
//|                                                     Telegram.mqh |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

Admin Home Kopfzeilen-Datei

In diesem Abschnitt entwickeln wir die Klasse CAdminHomeDialog, die als Hauptschnittstelle für die Administrationsoberfläche in unserem MQL5-Programm dient. Es integriert wichtige Header-Dateien für Dialog- und Schaltflächen-Steuerelemente und verwendet vordefinierte Konstanten, um konsistente Panel-Abmessungen und -Abstände zu gewährleisten.

//+------------------------------------------------------------------+
//| AdminHomeDialog.mqh                                              |
//+------------------------------------------------------------------+
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>

//+------------------------------------------------------------------+
//| Defines                                                          |
//+------------------------------------------------------------------+
#define ADMIN_PANEL_WIDTH   (335)
#define ADMIN_PANEL_HEIGHT  (350)
#define INDENT_LEFT         (11)
#define INDENT_TOP          (11)
#define INDENT_RIGHT        (11)
#define INDENT_BOTTOM       (11)
#define CONTROLS_GAP_X      (5)
#define CONTROLS_GAP_Y      (5)
#define BUTTON_WIDTH        (250)
#define BUTTON_HEIGHT       (40)

Die Klasse CAdminHomeDialog erweitert CAppDialog und enthält vier Hauptschaltflächen, m_tradeMgmtButton, m_commButton, m_analyticsButton und m_showAllButton, die eine reibungslose Navigation zu den verschiedenen Abschnitten des Verwaltungsbereichs ermöglichen. Die Klassenstruktur bleibt schlank, mit einem minimalen Konstruktor und Destruktor, während die Create-Methode sicherstellt, dass alle Schaltflächen für eine reibungslose Nutzererfahrung richtig initialisiert werden.

//+------------------------------------------------------------------+
//| CAdminHomeDialog class                                           |
//+------------------------------------------------------------------+
class CAdminHomeDialog : public CAppDialog
{
private:
    CButton m_tradeMgmtButton;
    CButton m_commButton;
    CButton m_analyticsButton;
    CButton m_showAllButton;

public:
    CAdminHomeDialog(void) {}
    ~CAdminHomeDialog(void) {}

    virtual bool Create(const long chart, const string name, const int subwin,
                       const int x1, const int y1, const int x2, const int y2);
    virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
};

//+------------------------------------------------------------------+
//| Create                                                           |
//+------------------------------------------------------------------+
bool CAdminHomeDialog::Create(const long chart, const string name, const int subwin,
                             const int x1, const int y1, const int x2, const int y2)
{
    if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2))
        return false;

    if(!CreateTradeMgmtButton()) return false;
    if(!CreateCommButton()) return false;
    if(!CreateAnalyticsButton()) return false;
    if(!CreateShowAllButton()) return false;

    return true;
}

Nutzerinteraktionen werden in der Methode OnEvent behandelt, in der Schaltflächenklicks Debug-Meldungen auslösen und ihre jeweiligen Ereignisbehandlung aufrufen: OnClickTradeManagement, OnClickCommunications, OnClickAnalytics und OnClickShowAll. Diese Handler protokollieren derzeit Interaktionen, werden aber im Zuge der Funktionserweiterung ausgebaut.

//+------------------------------------------------------------------+
//| Event Handling (Enhanced Debugging)                              |
//+------------------------------------------------------------------+
bool CAdminHomeDialog::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if(id == CHARTEVENT_OBJECT_CLICK)
    {
        Print("Clicked object: ", sparam); // Debug which object was clicked
        
        if(sparam == m_tradeMgmtButton.Name())
        {
            Print("Trade Management button detected");
            OnClickTradeManagement();
            return true;
        }
        else if(sparam == m_commButton.Name())
        {
            Print("Communications button detected");
            OnClickCommunications();
            return true;
        }
        else if(sparam == m_analyticsButton.Name())
        {
            Print("Analytics button detected");
            OnClickAnalytics();
            return true;
        }
        else if(sparam == m_showAllButton.Name())
        {
            Print("Show All button detected");
            OnClickShowAll();
            return true;
        }
    }
    return CAppDialog::OnEvent(id, lparam, dparam, sparam);
}

//+------------------------------------------------------------------+
//| Button Click Handlers                                            |
//+------------------------------------------------------------------+
void CAdminHomeDialog::OnClickTradeManagement() { Print("Trade Management Panel clicked"); }
void CAdminHomeDialog::OnClickCommunications()  { Print("Communications Panel clicked"); }
void CAdminHomeDialog::OnClickAnalytics()       { Print("Analytics Panel clicked"); }
void CAdminHomeDialog::OnClickShowAll()         { Print("Show All clicked"); }

Die Methoden zur Erstellung von Schaltflächen - CreateTradeMgmtButton, CreateCommButton, CreateAnalyticsButton und CreateShowAllButton - erzeugen dynamisch Schaltflächen mit eindeutigen Bezeichnern, präziser Positionierung und klar definierten Beschriftungen. Die Schaltfläche „Show All“ enthält sogar ein Emoji, um die Nutzeroberfläche zu verbessern. Im Zuge der weiteren Entwicklung werden weitere Verbesserungen und Verfeinerungen eingeführt, um die Leistung und Nutzerfreundlichkeit zu optimieren.

//+------------------------------------------------------------------+
//| Control Creation Methods                                         |
//+------------------------------------------------------------------+
bool CAdminHomeDialog::CreateTradeMgmtButton()
{
    int x = INDENT_LEFT;
    int y = INDENT_TOP;
    return m_tradeMgmtButton.Create(m_chart_id, m_name+"_TradeBtn", m_subwin,
                                  x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT)
        && m_tradeMgmtButton.Text("Trade Management Panel")
        && Add(m_tradeMgmtButton);
}

bool CAdminHomeDialog::CreateCommButton()
{
    int x = INDENT_LEFT;
    int y = INDENT_TOP + BUTTON_HEIGHT + CONTROLS_GAP_Y;
    return m_commButton.Create(m_chart_id, m_name+"_CommBtn", m_subwin,
                             x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT)
        && m_commButton.Text("Communications Panel")
        && Add(m_commButton);
}

bool CAdminHomeDialog::CreateAnalyticsButton()
{
    int x = INDENT_LEFT;
    int y = INDENT_TOP + (BUTTON_HEIGHT + CONTROLS_GAP_Y) * 2;
    return m_analyticsButton.Create(m_chart_id, m_name+"_AnalyticsBtn", m_subwin,
                                  x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT)
        && m_analyticsButton.Text("Analytics Panel")
        && Add(m_analyticsButton);
}

bool CAdminHomeDialog::CreateShowAllButton()
{
    int x = INDENT_LEFT;
    int y = INDENT_TOP + (BUTTON_HEIGHT + CONTROLS_GAP_Y) * 3;
    return m_showAllButton.Create(m_chart_id, m_name+"_ShowAllBtn", m_subwin,
                                x, y, x+BUTTON_WIDTH, y+BUTTON_HEIGHT)
        && m_showAllButton.Text("Show All 💥")
        && Add(m_showAllButton);
}

Implementierung der Datei AdminHomeDialog.mqh im Hauptprogramm:

1. Einbinden über #include "AdminHomeDialog.mqh"

#include "AdminHomeDialog.mqh"

Durch die Einbindung von AdminHomeDialog.mqh wird die Klasse CAdminHomeDialog im Hauptskript verfügbar. Ohne diese Einbindung würde der Compiler CAdminHomeDialog nicht erkennen, was zu Fehlern führen würde. Dieser modulare Ansatz ermöglicht es, das Hauptskript sauber zu halten, während die Implementierung des Dialogs in einer separaten Datei aufbewahrt wird, um die Organisation und Wartbarkeit zu verbessern.

2. Deklarieren von CAdminHomeDialog ExtDialog;

CAdminHomeDialog ExtDialog;

Die Deklaration von ExtDialog als Instanz von CAdminHomeDialog ermöglicht es dem Skript, im gesamten Programm auf das Admin Home Panel zu verweisen und es zu steuern. Dieses Objekt verwaltet die Erstellung, die Sichtbarkeit und die Ereignisverwaltung des Panels und macht es über verschiedene Funktionen hinweg zugänglich.

3. Erstellen von ExtDialog innerhalb von CreateHiddenPanels()

bool CreateHiddenPanels()
{
    bool success = ExtDialog.Create(0, "Admin Home", 0, 
                    MAIN_DIALOG_X, MAIN_DIALOG_Y, 
                    MAIN_DIALOG_X + MAIN_DIALOG_WIDTH, 
                    MAIN_DIALOG_Y + MAIN_DIALOG_HEIGHT);
    if(success) 
    {
        ExtDialog.Hide();
        ChartRedraw();
    }
    return success;
}

Die Methode Create() initialisiert das Panel mit bestimmten Abmessungen und positioniert es korrekt im Chart. Durch die Platzierung dieser Funktion innerhalb von CreateHiddenPanels() wird sichergestellt, dass das Panel nur einmal während der Initialisierung erstellt wird, wodurch der Einrichtungsprozess organisiert bleibt und eine unnötige Neuinitialisierung vermieden wird.

4. Angezeigt oder ausgeblendet je nach Authentifizierungsstatus in OnChartEvent()

if(authManager.IsAuthenticated())
{
    if(!ExtDialog.IsVisible())
    {
        ExtDialog.Show();
        ChartRedraw();
    }
    ExtDialog.ChartEvent(id, lparam, dparam, sparam);
}
else
{
    if(ExtDialog.IsVisible()) 
    {
        ExtDialog.Hide();
    }
}

Das Admin Home Panel sollte nur nach erfolgreicher Authentifizierung zugänglich sein. Überprüfung durch authManager.IsAuthenticated() stellt sicher, dass nicht autorisierte Nutzer nicht mit dem Panel interagieren können. Wenn die Authentifizierung gültig ist, wird das Panel angezeigt, andernfalls bleibt es verborgen, was die Sicherheit und die Zugangskontrolle verbessert.

5. Wird in OnDeinit() zerstört, wenn das Skript entfernt wird.

void OnDeinit(const int reason)
{
    ExtDialog.Destroy(reason);
}

Wenn der Experte aus dem Chart entfernt wird, stellt der Aufruf von ExtDialog.Destroy() sicher, dass die für das Panel zugewiesenen Ressourcen gründlich bereinigt werden. Dies verhindert mögliche Speicherlecks oder verwaiste grafische Objekte, die zukünftige Instanzen des Skripts beeinträchtigen könnten.

Header-Datei für Telegram

Um eine Header-Datei für Telegram zu erstellen, wird die Telegram-Funktion direkt in den Header-Source kopiert, da sie einfach und unkompliziert ist. Bei anderen Dateien, die einen strukturierteren Aufbau mit Klassen, Methoden, Konstruktoren und Destruktoren erfordern, kann dieser Ansatz jedoch anders aussehen, wie bereits erwähnt. Daher ist dies die einfachste Header-Datei in unserer Liste der Kreationen. Durch die Modularisierung der Funktion reduzieren wir die Länge des Hauptcodes, und die Funktion kann leicht in anderen Projekten wiederverwendet werden, wo sie benötigt wird.

//+------------------------------------------------------------------+
//|                                                     Telegram.mqh |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
//+------------------------------------------------------------------+
//| Telegram.mqh - Telegram Communication Include File               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Send the message to Telegram                                     |
//+------------------------------------------------------------------+
bool SendMessageToTelegram(string message, string chatId, string botToken)
  {
   string url = "https://api.telegram.org/bot" + botToken + "/sendMessage";
   string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}";

   char postData[];
   ArrayResize(postData, StringToCharArray(jsonMessage, postData) - 1);

   int timeout = 5000;
   char result[];
   string responseHeaders;
   int responseCode = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, postData, result, responseHeaders);

   if (responseCode == 200)
     {
      Print("Message sent successfully: ", message);
      return true;
     }
   else
     {
      Print("Failed to send message. HTTP code: ", responseCode, " Error code: ", GetLastError());
      Print("Response: ", CharArrayToString(result));
      return false;
     }
  }
//+------------------------------------------------------------------+

Implementierung des Telegram-Headers in den Hauptcode

Dies geschieht einfach in 2 Schritten:

1. Wir fügen die Datei wie folgt in den Hauptcode ein.

#include<Telegram.mqh>

Das obige funktioniert nur, wenn es im Verzeichnis MQL5/Include gespeichert ist, andernfalls muss der Name des Unterverzeichnisses wie folgt angegeben werden

#include <FolderName\Telegram.mqh> // Replace FolderName with actual location name

2. Schließlich müssen wir die Funktion in Ihrem Hauptcode aufrufen, wenn sie benötigt wird, wie folgt:

SendMessageToTelegram("Your verification code: " + ActiveTwoFactorAuthCode, 
                         TwoFactorAuthChatId, TwoFactorAuthBotToken)

Entwickeln einer Header-Datei für die Authentifizierung

Aus früheren Entwicklungen kennen Sie die Entwicklung der Logik der Sicherheitsabfragen, die konsequent in jeder Version des Admin-Panels verwendet wurde. Diese Logik kann auch für andere Projekte im Zusammenhang mit dem Panel angepasst werden, insbesondere bei der Entwicklung eines klassifizierten Moduls für dessen Funktionalität. In dieser Phase entwickeln wir die Datei Authentication.mqh, in der die gesamte Sicherheitslogik des früheren Admin-Panels zusammengefasst ist. Ich werde den Code unten weitergeben und dann erklären, wie er funktioniert.

//+------------------------------------------------------------------+
//|                                          authentication.mqh      |
//|                           Copyright 2024, Clemence Benjamin      |
//|        https://www.mql5.com/en/users/billionaire2024/seller      |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.0"
#property strict

// Authentication Dialog Coordinates
#define AUTH_DIALOG_X         100
#define AUTH_DIALOG_Y         100
#define AUTH_DIALOG_WIDTH     300
#define AUTH_DIALOG_HEIGHT    200

#define PASS_INPUT_X          20
#define PASS_INPUT_Y          50
#define PASS_INPUT_WIDTH      260  // Wider input field
#define PASS_INPUT_HEIGHT     30

#define PASS_LABEL_X          20
#define PASS_LABEL_Y          20
#define PASS_LABEL_WIDTH      200
#define PASS_LABEL_HEIGHT     20

#define FEEDBACK_LABEL_X      20
#define FEEDBACK_LABEL_Y      100
#define FEEDBACK_LABEL_WIDTH  260
#define FEEDBACK_LABEL_HEIGHT 40

// Button spacing adjustments
#define LOGIN_BTN_X           20
#define LOGIN_BTN_Y           130
#define LOGIN_BTN_WIDTH       120
#define LOGIN_BTN_HEIGHT      30

#define CANCEL_BTN_X          160  // Added 20px spacing from login button
#define CANCEL_BTN_Y          130
#define CANCEL_BTN_WIDTH      120
#define CANCEL_BTN_HEIGHT     30

// Two-Factor Authentication Dialog Coordinates
#define TWOFA_DIALOG_X        100
#define TWOFA_DIALOG_Y        100
#define TWOFA_DIALOG_WIDTH    300
#define TWOFA_DIALOG_HEIGHT   200

#define TWOFA_INPUT_X         20
#define TWOFA_INPUT_Y         50
#define TWOFA_INPUT_WIDTH     180
#define TWOFA_INPUT_HEIGHT    30

#define TWOFA_LABEL_X         20
#define TWOFA_LABEL_Y         20
#define TWOFA_LABEL_WIDTH     260
#define TWOFA_LABEL_HEIGHT    20

#define TWOFA_FEEDBACK_X      20
#define TWOFA_FEEDBACK_Y      100
#define TWOFA_FEEDBACK_WIDTH  260
#define TWOFA_FEEDBACK_HEIGHT 40

#define TWOFA_VERIFY_BTN_X    60
#define TWOFA_VERIFY_BTN_Y    130
#define TWOFA_VERIFY_WIDTH    120
#define TWOFA_VERIFY_HEIGHT   30

#define TWOFA_CANCEL_BTN_X    140
#define TWOFA_CANCEL_BTN_Y    130
#define TWOFA_CANCEL_WIDTH    60
#define TWOFA_CANCEL_HEIGHT   30

#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>
#include <Telegram.mqh>

class CAuthenticationManager {
private:
    CDialog m_authDialog;
    CDialog m_2faDialog;
    CEdit m_passwordInput;
    CEdit m_2faCodeInput;
    CLabel m_passwordLabel;
    CLabel m_feedbackLabel;
    CLabel m_2faLabel;
    CLabel m_2faFeedback;
    CButton m_loginButton;
    CButton m_closeAuthButton;
    CButton m_2faLoginButton;
    CButton m_close2faButton;

    string m_password;
    string m_2faChatId;
    string m_2faBotToken;
    int m_failedAttempts;
    bool m_isAuthenticated;
    string m_active2faCode;

public:
    CAuthenticationManager(string password, string twoFactorChatId, string twoFactorBotToken) :
        m_password(password),
        m_2faChatId(twoFactorChatId),
        m_2faBotToken(twoFactorBotToken),
        m_failedAttempts(0),
        m_isAuthenticated(false),
        m_active2faCode("")
    {
    }

    ~CAuthenticationManager()
    {
        m_authDialog.Destroy();
        m_2faDialog.Destroy();
    }

    bool Initialize() {
        if(!CreateAuthDialog() || !Create2FADialog()) {
            Print("Authentication initialization failed");
            return false;
        }
        m_2faDialog.Hide();  // Ensure 2FA dialog starts hidden
        return true;
    }

    bool IsAuthenticated() const { return m_isAuthenticated; }

    void HandleEvent(const int id, const long &lparam, const double &dparam, const string &sparam) {
        if(id == CHARTEVENT_OBJECT_CLICK) {
            if(sparam == "LoginButton") HandleLoginAttempt();
            else if(sparam == "2FALoginButton") Handle2FAAttempt();
            else if(sparam == "CloseAuthButton") m_authDialog.Hide();
            else if(sparam == "Close2FAButton") m_2faDialog.Hide();
        }
    }

private:
    bool CreateAuthDialog() {
    if(!m_authDialog.Create(0, "Authentication", 0, 
       AUTH_DIALOG_X, AUTH_DIALOG_Y, 
       AUTH_DIALOG_X + AUTH_DIALOG_WIDTH, 
       AUTH_DIALOG_Y + AUTH_DIALOG_HEIGHT)) 
       return false;

    if(!m_passwordInput.Create(0, "PasswordInput", 0, 
       PASS_INPUT_X, PASS_INPUT_Y, 
       PASS_INPUT_X + PASS_INPUT_WIDTH, 
       PASS_INPUT_Y + PASS_INPUT_HEIGHT) ||
       !m_passwordLabel.Create(0, "PasswordLabel", 0, 
       PASS_LABEL_X, PASS_LABEL_Y, 
       PASS_LABEL_X + PASS_LABEL_WIDTH, 
       PASS_LABEL_Y + PASS_LABEL_HEIGHT) ||
       !m_feedbackLabel.Create(0, "AuthFeedback", 0, 
       FEEDBACK_LABEL_X, FEEDBACK_LABEL_Y, 
       FEEDBACK_LABEL_X + FEEDBACK_LABEL_WIDTH, 
       FEEDBACK_LABEL_Y + FEEDBACK_LABEL_HEIGHT) ||
       !m_loginButton.Create(0, "LoginButton", 0, 
       LOGIN_BTN_X, LOGIN_BTN_Y, 
       LOGIN_BTN_X + LOGIN_BTN_WIDTH, 
       LOGIN_BTN_Y + LOGIN_BTN_HEIGHT) ||
       !m_closeAuthButton.Create(0, "CloseAuthButton", 0, 
       CANCEL_BTN_X, CANCEL_BTN_Y, 
       CANCEL_BTN_X + CANCEL_BTN_WIDTH, 
       CANCEL_BTN_Y + CANCEL_BTN_HEIGHT))
       return false;       

        m_passwordLabel.Text("Enter Password:");
        m_feedbackLabel.Text("");
        m_feedbackLabel.Color(clrRed);
        m_loginButton.Text("Login");
        m_closeAuthButton.Text("Cancel");

        m_authDialog.Add(m_passwordInput);
        m_authDialog.Add(m_passwordLabel);
        m_authDialog.Add(m_feedbackLabel);
        m_authDialog.Add(m_loginButton);
        m_authDialog.Add(m_closeAuthButton);
        
        m_authDialog.Show();
        return true;
    }

    bool Create2FADialog() {
        if(!m_2faDialog.Create(0, "2FA Verification", 0, 
           TWOFA_DIALOG_X, TWOFA_DIALOG_Y, 
           TWOFA_DIALOG_X + TWOFA_DIALOG_WIDTH, 
           TWOFA_DIALOG_Y + TWOFA_DIALOG_HEIGHT))
            return false;

        if(!m_2faCodeInput.Create(0, "2FAInput", 0, 
           TWOFA_INPUT_X, TWOFA_INPUT_Y, 
           TWOFA_INPUT_X + TWOFA_INPUT_WIDTH, 
           TWOFA_INPUT_Y + TWOFA_INPUT_HEIGHT) ||
           !m_2faLabel.Create(0, "2FALabel", 0, 
           TWOFA_LABEL_X, TWOFA_LABEL_Y, 
           TWOFA_LABEL_X + TWOFA_LABEL_WIDTH, 
           TWOFA_LABEL_Y + TWOFA_LABEL_HEIGHT) ||
           !m_2faFeedback.Create(0, "2FAFeedback", 0, 
           TWOFA_FEEDBACK_X, TWOFA_FEEDBACK_Y, 
           TWOFA_FEEDBACK_X + TWOFA_FEEDBACK_WIDTH, 
           TWOFA_FEEDBACK_Y + TWOFA_FEEDBACK_HEIGHT) ||
           !m_2faLoginButton.Create(0, "2FALoginButton", 0, 
           TWOFA_VERIFY_BTN_X, TWOFA_VERIFY_BTN_Y, 
           TWOFA_VERIFY_BTN_X + TWOFA_VERIFY_WIDTH, 
           TWOFA_VERIFY_BTN_Y + TWOFA_VERIFY_HEIGHT) ||
           !m_close2faButton.Create(0, "Close2FAButton", 0, 
           TWOFA_CANCEL_BTN_X, TWOFA_CANCEL_BTN_Y, 
           TWOFA_CANCEL_BTN_X + TWOFA_CANCEL_WIDTH, 
           TWOFA_CANCEL_BTN_Y + TWOFA_CANCEL_HEIGHT))
            return false;

        m_2faLabel.Text("Enter verification code:");
        m_2faFeedback.Text("");
        m_2faFeedback.Color(clrRed);
        m_2faLoginButton.Text("Verify");
        m_close2faButton.Text("Cancel");

        m_2faDialog.Add(m_2faCodeInput);
        m_2faDialog.Add(m_2faLabel);
        m_2faDialog.Add(m_2faFeedback);
        m_2faDialog.Add(m_2faLoginButton);
        m_2faDialog.Add(m_close2faButton);
        
        return true;
    }

    void HandleLoginAttempt() {
        if(m_passwordInput.Text() == m_password) {
            m_isAuthenticated = true;
            m_authDialog.Hide();
            m_2faDialog.Hide();  // Ensure both dialogs are hidden
        } else {
            if(++m_failedAttempts >= 3) {
                Generate2FACode();
                m_authDialog.Hide();
                m_2faDialog.Show();
            } else {
                m_feedbackLabel.Text(StringFormat("Invalid password (%d attempts left)", 
                                                 3 - m_failedAttempts));
            }
        }
    }

    void Handle2FAAttempt() {
        if(m_2faCodeInput.Text() == m_active2faCode) {
            m_isAuthenticated = true;
            m_2faDialog.Hide();
            m_authDialog.Hide();  // Hide both dialogs on success
        } else {
            m_2faFeedback.Text("Invalid code - please try again");
            m_2faCodeInput.Text("");
        }
    }

    void Generate2FACode() {
        m_active2faCode = StringFormat("%06d", MathRand() % 1000000);
        SendMessageToTelegram("Your verification code: " + m_active2faCode, 
                             m_2faChatId, m_2faBotToken);
    }
};
//+------------------------------------------------------------------+

Die Klasse CAuthenticationManager in dem bereitgestellten Modul verwaltet einen mehrstufigen Nutzerauthentifizierungsprozess für eine MQL5-basierte Anwendung. Es verwaltet sowohl die Passwort-Authentifizierung als auch die Zwei-Faktor-Authentifizierung (2FA) über eine dialogbasierte Schnittstelle. Der Authentifizierungsprozess beginnt damit, dass der Nutzer ein Passwort in ein Dialogfeld eingibt. Wenn das Kennwort korrekt ist, wird der Zugang sofort gewährt. Wenn die Eingabe des Passworts jedoch fehlschlägt, verfolgt das System die fehlgeschlagenen Versuche, und nach drei falschen Eingaben wird ein zweiter Dialog für 2FA ausgelöst. Beim 2FA-Verfahren wird ein sechsstelliger Verifizierungscode generiert, der über Telegram an den Nutzer gesendet wird, der den richtigen Code eingeben muss, um fortzufahren.

Das Modul verwendet vordefinierte Koordinaten für Dialogelemente, um ein konsistentes Layout zu gewährleisten, und enthält Feedback-Mechanismen, um Nutzer über Fehler oder eine erfolgreiche Authentifizierung zu informieren. Die Klasse integriert auch die Bibliothek Telegram.mqh, um das Senden von 2FA-Codes zu ermöglichen. Bei der Entwicklung wurde auf Erweiterbarkeit geachtet, sodass die Passwort- und 2FA-Einstellungen leicht geändert werden können und beide Dialoge bei erfolgreicher Authentifizierung ausgeblendet werden. Dieses Design optimiert die Nutzerfreundlichkeit und bietet gleichzeitig zuverlässige Sicherheitsfunktionen.


Das neue Handelsadministrator-Panel

In diesem Stadium integrieren wir alle zuvor entwickelten Module, um ein strukturierteres und effizienteres Admin-Panel aufzubauen. Diese verbesserte Version verbessert die Organisation und Modularität, sodass die Komponenten leicht gemeinsam genutzt und in anderen Anwendungen innerhalb des Terminals wiederverwendet werden können.

 Das New Admin Panel bindet AdminHomeDialog.mqhfür die grafische Oberfläche ein und Authentication.mqh für die Authentifizierungsverwaltung. Der EA definiert Eingabeparameter für eine Telegram-Chat-ID und ein Bot-Token für die 2FA-Verifizierung. Während der Initialisierung (OnInit) wird versucht, die Authentifizierung zu initialisieren und ein ausgeblendetes Panel zu erstellen (CreateHiddenPanels), was fehlschlägt, wenn einer der beiden Prozesse nicht erfolgreich ist.

Die Funktion OnChartEvent verarbeitet Chartsreignisse und behandelt die Authentifizierung, bevor das Panel AdminHomeDialog je nach Authentifizierungsstatus ein- oder ausgeblendet wird. Wenn die Authentifizierung erfolgreich war, stellt es sicher, dass das Panel sichtbar ist und leitet Ereignisse an das Panel weiter. Andernfalls wird das Bedienfeld ausgeblendet. Die Deinitialisierungsfunktion (OnDeinit) stellt sicher, dass der Dialog ordnungsgemäß gelöscht wird. Dieses Design gewährleistet einen sicheren Zugriff auf das Admin Panel Home und erfordert eine Authentifizierung, bevor die Kontrolle über die Funktionen gewährt wird.

Hier ist der vollständige Code für das neue Programm:

//+------------------------------------------------------------------+
//|                                              New Admin Panel.mq5 |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.00"

// Panel coordinate defines
#define MAIN_DIALOG_X        30
#define MAIN_DIALOG_Y        80
#define MAIN_DIALOG_WIDTH    335
#define MAIN_DIALOG_HEIGHT   350

#include "AdminHomeDialog.mqh"
#include <Authentication.mqh>

// Input parameters for authentication
input string TwoFactorChatID = "YOUR_CHAT_ID";
input string TwoFactorBotToken = "YOUR_BOT_TOKEN";
string AuthPassword = "2024";

CAdminHomeDialog ExtDialog;
CAuthenticationManager authManager(AuthPassword, TwoFactorChatID, TwoFactorBotToken);

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{  
    if(!authManager.Initialize() || !CreateHiddenPanels())
    {
        Print("Initialization failed");
        return INIT_FAILED;
    }
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    ExtDialog.Destroy(reason);
}

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
    authManager.HandleEvent(id, lparam, dparam, sparam);
    
    if(authManager.IsAuthenticated())
    {
        if(!ExtDialog.IsVisible())
        {
            ExtDialog.Show();
            ChartRedraw();
        }
        // Handle dialog events only when authenticated
        ExtDialog.ChartEvent(id, lparam, dparam, sparam);
    }
    else
    {
        if(ExtDialog.IsVisible()) 
        {
            ExtDialog.Hide();
            
        }
    }
}

//+------------------------------------------------------------------+
//| Create hidden panels                                             |
//+------------------------------------------------------------------+
bool CreateHiddenPanels()
{
    bool success = ExtDialog.Create(0, "Admin Home", 0, 
                    MAIN_DIALOG_X, MAIN_DIALOG_Y, 
                    MAIN_DIALOG_X + MAIN_DIALOG_WIDTH, 
                    MAIN_DIALOG_Y + MAIN_DIALOG_HEIGHT);
    if(success) 
    {
        ExtDialog.Hide();
        ChartRedraw();
    }
    return success;
}


Tests

Hier stellen wir die Testergebnisse vor, nachdem wir einzelne Komponenten entwickelt und logisch in das neue Admin Panel integriert haben. Wir haben das Programm erfolgreich kompiliert und auf dem Chart ausgeführt, wie unten gezeigt.

Testen des neuen Administrator-Panels

Testen des neuen Administrator-Panels auf einem EURUSD-Chart



Schlussfolgerung

Sie werden sicher zu schätzen wissen, wie wir die Organisation unseres Verwaltungsbereichs gestrafft haben. Durch die Vererbung von CAppDialog haben wir die Reaktionsfähigkeit der Anwendung erheblich verbessert, sodass wir das Panel zum ersten Mal frei über das Chart ziehen können. Außerdem können wir mit der Schaltfläche zum Minimieren, die von der Basisklasse geerbt wurde, die Anwendung minimieren, sodass die Chartansicht nicht beeinträchtigt wird, während sie im Hintergrund weiterläuft. Das Panel kann bei Bedarf jederzeit maximiert werden, um eine nahtlose Nutzung zu gewährleisten.

Unser Fokus auf Lesbarkeit, Skalierbarkeit und Modularität hat uns dazu gebracht, jede Komponente separat zu entwickeln. Dieser Ansatz verbessert die Wiederverwendbarkeit des Codes und ermöglicht es uns, diese Module in andere Programme zu integrieren, indem wir einfach die entsprechenden Dateien einbinden und wichtige Methoden aufrufen. Wir planen, die verbleibenden Komponenten zu vervollständigen, die das neue Admin Panel noch leistungsfähiger machen werden. Dazu gehören CommunicationsDialog.mqh, TradeManagementDialog.mqh und AnalyticsDialog.mqh, die jeweils so konzipiert sind, dass sie in verschiedenen Anwendungen wiederverwendet werden können.

Als Ausgangspunkt können Sie versuchen, Telegram.mqh in Ihr eigenes Programm zu implementieren, um zu sehen, wie einfach es sich integrieren lässt. Ich habe alle notwendigen Dateien unten angehängt - viel Spaß beim Testen und Weiterentwickeln!

Datei Beschreibung
New Admin Panel .mqh Das neueste Admin Panel beinhaltet das Konzept der Modularität.
Telegram.mqh Zur Übermittlung von Nachrichten und Benachrichtigungen über Telegram.
Authentication.mqh Diese Datei enthält alle Sicherheitserklärungen und die gesamte Logik.
AdminHomeDialog.mqh Für die Erstellung des Admin-Home-Dialogs. Sie enthält alle Koordinatenangaben.

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

Beigefügte Dateien |
Telegram.mqh (1.62 KB)
Authentication.mqh (8.92 KB)
AdminHomeDialog.mqh (12.02 KB)
Meistern der Log-Einträge (Teil 5): Optimierungen mit Cache und Rotation Meistern der Log-Einträge (Teil 5): Optimierungen mit Cache und Rotation
Dieser Artikel verbessert die Logging-Bibliothek durch Hinzufügen von Formatierern durch die Klasse CIntervalWatcher zur Verwaltung von Ausführungszyklen, Optimierung mit Caching und Dateirotation, Leistungstests und praktischen Beispielen. Mit diesen Verbesserungen gewährleisten wir ein effizientes, skalierbares und anpassungsfähiges Protokollierungssystem für unterschiedliche Entwicklungsszenarien.
Datenwissenschaft und ML (Teil 33): Pandas Dataframe in MQL5, Vereinfachung der Datensammlung für ML-Nutzung Datenwissenschaft und ML (Teil 33): Pandas Dataframe in MQL5, Vereinfachung der Datensammlung für ML-Nutzung
Bei der Arbeit mit maschinellen Lernmodellen ist es wichtig, die Konsistenz der für Training, Validierung und Tests verwendeten Daten sicherzustellen. In diesem Artikel werden wir unsere eigene Version der Pandas-Bibliothek in MQL5 erstellen, um einen einheitlichen Ansatz für den Umgang mit maschinellen Lerndaten zu gewährleisten und sicherzustellen, dass innerhalb und außerhalb von MQL5, wo der Großteil des Trainings stattfindet, dieselben Daten verwendet werden.
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 14): Parabolischer Stopp und Umkehr-Tool Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 14): Parabolischer Stopp und Umkehr-Tool
Der Einsatz von technischen Indikatoren in der Preisaktionsanalyse ist ein wirkungsvoller Ansatz. Diese Indikatoren weisen häufig auf wichtige Umkehr- und Rücksetzpunkte hin und bieten wertvolle Einblicke in die Marktdynamik. In diesem Artikel zeigen wir, wie wir ein automatisiertes Tool entwickelt haben, das mit Hilfe des Parabolic-SAR-Indikators Signale erzeugt.
Einführung in MQL5 (Teil 12): Ein Anfängerleitfaden für das Erstellen nutzerdefinierter Indikatoren Einführung in MQL5 (Teil 12): Ein Anfängerleitfaden für das Erstellen nutzerdefinierter Indikatoren
Erfahren Sie, wie Sie einen nutzerdefinierten Indikator in MQL5 erstellen können. Mit einem projektbezogenen Ansatz. Dieser einsteigerfreundliche Leitfaden behandelt Indikatorpuffer, Eigenschaften und Trendvisualisierung und ermöglicht es Ihnen, Schritt für Schritt zu lernen.