English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
MQL für Anfänger: Wie man Objektklassen entwirft und baut

MQL für Anfänger: Wie man Objektklassen entwirft und baut

MetaTrader 5Experten | 12 Februar 2016, 10:03
2 132 0
Sergey Pavlov
Sergey Pavlov

Einführung in Objekt-orientiertes Programmieren (OOP)

Anfängerfrage: Wenn man nur eine vage Ahnung von prozeduralem Programmieren hat, kann man dann OOP beherrschen und es zum Schreiben von automatisierten Handelsstrategien verwenden? Oder übersteigt diese Aufgabe die Kenntnisse des gewöhnlichen Anwenders?

Im Großen und Ganzen ist es möglich, die Objekt-orientierte Programmiersprache zum Schreiben eines MQL5 Expert Advisors oder Indikators zu benutzen, ohne dazu die Prinzipien des Objekt-orientierten Programmierens anwenden zu müssen. Sie müssen nicht unbedingt neue Technologien in Ihren Entwicklungen verwenden. Wählen Sie den Weg, der Ihnen am einfachsten erscheint. Zudem kann die Anwendung des OOP darüber hinaus nicht die Rentabilität von Handelsrobotern garantieren, die Sie erzeugen.

Dennoch ermöglicht der Übergang zu einem neuen (Objekt-orientierten) Ansatz die Tür zur Anwendung komplexerer adaptiver Handelsstrategie-mathematischer Modelle auf ihre Expert Advisor, die auf externe Veränderungen reagieren und sich mit dem Markt synchronisieren.

Also sehen wir uns die Technologien mal an, auf denen das OOP beruht:

  1. Ereignisse
  2. Objektklassen

Ereignisse sind die primäre Basis von OOP. Die gesamte Programmlogik ist auf der Verarbeitung von konstant ankommenden Ereignissen aufgebaut. Die entsprechenden Reaktionen auf diese Ereignisse werden in den Objektklassen festgelegt und beschrieben. Anders gesagt: ein Klassenobjekt arbeitet durch Abfangen und Verarbeiten des Ereignis-Flows.

Die zweite Basis ist die Klasse der Objekte, die ihrerseits wiederum auf "drei Säulen" ruht:

  1. Verkapselung - Schutz der Klasse auf Basis eines "Black-Box" Prinzips: das Objekt reagiert auf Ereignisse, aber seine tatsächliche Implementierung bleibt unbekannt. 
  2. Vererbung - Möglichkeit der Erzeugung einer neuen Klasse aus einer bestehenden unter Beibehaltung aller Eigenschaften und Methoden der "Vorgänger"-Klasse.
  3. Polymorphismus - Möglichkeit der Veränderung der Implementierung einer ererbten Methode in eine "Nachfolger"-Klasse.

Basiskonzepte kann am besten im Expert Advisor-Code darstellen.

Ereignisse:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()                        // OnInit event processing
  {
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)     // OnDeInit event processing
  {
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()                       // OnTick event processing
  {
  }
//+------------------------------------------------------------------+
//| Expert Timer function                                            |
//+------------------------------------------------------------------+
void OnTimer()                      // OnTimer event processing
  {
  }
//+------------------------------------------------------------------+
//| Expert Chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,     // OnChartEvent event processing
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
  }

Objektklasse:

class CNew:public CObject
  {
private:
   int               X,Y;
   void              EditXY();
protected:
   bool              on_event;      //events processing flag
public:
   // Class constructor
   void              CNew();
   // OnChart event processing method
   virtual void      OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Verkapselung:

private:
   int               X,Y;
   void              EditXY();

Vererbung:

class CNew: public CObject

Polymorphismus:

   // OnChart event processing method
   virtual void      OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);

Der virtuelle Modifikator dieser Methode bedeutet, dass der OnEvent Handler außer Kraft gesetzt werden kann, doch der Name der Methode bleibt in unserem Fall der gleiche wie der der Vorgänger-Klasse.

2. Klassen entwerfen

Einer der bedeutendsten Vorteile der OOP ist seine Ausbaufähigkeit - d.h. das bestehende System kann mit neuen Komponenten arbeiten, ohne dass man diese verändern muss. Zu diesem Zeitpunkt können neue Komponenten hinzugefügt werden.

Sehen wir uns den Designvorgang an, indem wir ein virtuelles Designprogramm von MasterWindows Klassen für MQL5 erzeugen.

2,1. Phase I: Projektentwurf

Der Designprozess beginnt mit einer Skizze, die mit Bleistift auf ein Blatt Papier gezeichnet wird. Dies ist einer der anspruchsvollsten und aufregendsten Momente beim Programmieren. Wir müssen hier nicht nur den Dialog zwischen dem Programm und dem Benutzer (Interface) bedenken, sondern auch die Organisation der Datenverarbeitung. Das kann durchaus mehr als einen Tag dauern. Am besten beginnt man hier mit dem Interface, da es in manchen Fällen, so wie in unserem Beispiel, bestimmend werden kann,wenn man den Algorithmus strukturiert.

Zur Organisation des Dialogs des erzeugten Programms verwenden wir das Formular (ähnlich wie im Windows Anwendungsfenster (vgl. Schaubild in Abb. 1). Es enthält Zeilen, die wiederum aus Zellen und Zellen graphischer Objekte bestehen. Und damit beginnen wir schon jetzt, obwohl wir uns erst in der konzeptuellen Designphase befinden, die Programmstruktur und die Klassifikation der Objekte zu erkennen.

Abb. 1 Formular des Klassen-Constructors (Schaubild)

Abb. 1 Formular des Klassen-Constructors (Schaubild)

Trotz einer ausreichend großen Zahl an Reihen und Zellen (Feldern), bestehen sie dennoch nur aus zwei Arten von graphischen Objekten: OBJ_EDIT und OBJ_BUTTON . Sobald wir also das visuelle Erscheinungsbild, die Struktur und die vom Programm erzeugten Basisobjekte festgelegt haben, können wir davon ausgehen, dass der Designentwurf fertig ist und unser der nächsten Phase zuwenden.

2.2 Phase II: Design der Basisklasse

Es gibt drei solcher Klassen und es können (ggf.) später weitere hinzugefügt werden:

  • Zellen-Klasse CCell;
  • Reihen-Klasse CRow, besteht aus Zellen der Klasse CCell;
  • Fenster-Klasse CWin, besteht aus Zeilen der Klasse CRow.

Wir können jetzt direkt zum Programmieren der Klassen weitergehen, doch... Moment, wir müssen zuvor noch eine sehr wichtige Aufgabe erledigen - der Datenaustausch zwischen Objekten der Klassen. Zu diesem Zweck enthält die MQL5-Sprache, neben den üblichen Variablen, einen neuen Typ - Struktur . Natürlich sehen wir in dieser Designphase noch nicht alle Verbindungen vollständig und es fällt uns auch schwer, sie zu berechnen. Und daher füllen wir im Verlauf des Projekts, Schritt für Schritt die Beschreibung der Klassen und Strukturen. Des Weiteren stehen die Prinzipien des OOP dem nicht nur nicht im Weg, sondern ganz im Gegenteil - sie unterstützen sogar die Technologie oder das Programmieren.

WinCell Struktur:

struct WinCell
  {
   color             TextColor;     // text color
   color             BGColor;       // background color
   color             BGEditColor;   // background color while editing
   ENUM_BASE_CORNER  Corner;         // anchor corner
   int               H;            // cell height
   int               Corn;         // displacement direction (1;-1)
  };

Strukturen, die keine Strings und Objekte dynamischer Arrays enthalten, nennt man einfache Strukturen. Die Variablen solcher Strukturen können ungehindert ineinander kopiert werden, selbst wenn die Strukturen unterschiedlich sind. Die somit erreichte Struktur ist genau dieser Art Ihre Effektivität bewerten wir später.

Basisklasse CCell:

//+------------------------------------------------------------------+
//| CCell base class                                                 |
//+------------------------------------------------------------------+
class CCell
  {
private:
protected:
   bool              on_event;      // event processing flag
   ENUM_OBJECT       type;           // cell type
public:
   WinCell           Property;     // cell property
   string            name;          // cell name
   //+---------------------------------------------------------------+
   // Class constructor
   void              CCell();
   virtual     // Draw method
   void              Draw(string m_name,
                          int m_xdelta,
                          int m_ydelta,
                          int m_bsize);
   virtual     // Event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Basisklasse CRow:

//+------------------------------------------------------------------+
//| CRow base class                                                  |
//+------------------------------------------------------------------+
class CRow
  {
protected:
   bool              on_event;      // event processing flag
public:
   string            name;          // row name
   WinCell           Property;     // row property
   //+---------------------------------------------------------------+
   // Class constructor
   void              CRow();
   virtual     // Draw method
   void              Draw(string m_name,
                          int m_xdelta,
                          int m_ydelta,
                          int m_bsize);
   virtual     // Event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Basisklasse CWin:

//+------------------------------------------------------------------+
//| Base CWin class (WINDOW)                                         |
//+------------------------------------------------------------------+
class CWin
  {
private:
   void              SetXY(int m_corner); //Coordinates
protected:
   bool              on_event;   // event processing flag
public:
   string            name;       // window name
   int               w_corner;   // window corner
   int               w_xdelta;   // vertical delta
   int               w_ydelta;   // horizontal detla
   int               w_xpos;     // X coordinate
   int               w_ypos;     // Y coordinate
   int               w_bsize;    // Window width
   int               w_hsize;    // Window height
   int               w_h_corner; // hide mode corner
   WinCell           Property;   // Property
   //---
   CRowType1         STR1;       // CRowType1
   CRowType2         STR2;       // CRowType2
   CRowType3         STR3;       // CRowType3
   CRowType4         STR4;       // CRowType4
   CRowType5         STR5;       // CRowType5
   CRowType6         STR6;       // CRowType6
   //+---------------------------------------------------------------+
   // Class constructor
   void              CWin();
   // Set window properties
   void              SetWin(string m_name,
                            int m_xdelta,
                            int m_ydelta,
                            int m_bsize,
                            int m_corner);
   virtual     // Draw window method
   void              Draw(int &MMint[][3],
                          string &MMstr[][3],
                          int count);
   virtual     // OnEventTick handler
   void              OnEventTick();
   virtual     // OnChart event handler method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Erklärungen und Empfehlungen:

  • Alle dieser Basisklassen (in unserem Projekt) enthalten Methoden zur Verarbeitung von Ereignissen. Sie sind für das Abfangen und Übertragen von Ereignissen an weiteren Punkten der Kette notwendig. Ohne einen Empfangs- und Sendemechanismus für Ereignisse, verliert das Programm (oder Modul) seine Interaktivität.
  • Bei der Entwicklung einer Basisklasse sollte man darauf achten, nur ein Mindestmaß an Methoden zu verwenden. Danach kann man die "nachfolgenden" Klassen mit verschiedenen Erweiterungen dieser Klasse ausstatten, die die Funktionalität der erzeugten Objekte erhöhen.
  • Bitte niemals einen direkten Aufruf an die internen Daten einer anderen Klasse verwenden!

2,3. Phase III: Arbeitsprojekt

In dieser Phase beginnen wir mit der schrittweisen Erzeugung des Programms. Beginnend mit dem unterstützenden Framework werden wir die funktionalen Komponenten des Programms steigern und es mit Inhalt füllen. Dabei kontrollieren wir die ganze Zeit über die Exaktheit der Arbeit, wenden eine Fehlersuche mit einem optimierten Code an und verfolgen alle sich zeigenden Fehler.

Halten wir hier kurz an und betrachten uns die Technologie der Erzeugung des Frameworks, das für fast jedes Programm so funktioniert. Seine Hauptvoraussetzung -es sollte sofort betriebsfähig sein (ohne Fehler erstellen und bei Ausführung sofort laufen). Darum haben sich die Sprach-Designer gekümmert und raten daher zum Einsatz des Expert Advisor Templates als Framework, dass vom MQL5-Assistenten generiert wird.

Betrachten wir als Beispiel unsere eigene Version dieses Templates:

1) Programm = Expert Advisor

//+------------------------------------------------------------------+
//|                                                MasterWindows.mq5 |
//|                                                 Copyright DC2008 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "DC2008"
#property link      "http://www.mql5.com"
#property version   "1.00"
//--- include files with classes
#include <ClassMasterWindows.mqh>
//--- Main module declaration
CMasterWindows    MasterWin;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Launch of the main module
   MasterWin.Run();
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Deinitialization of the main module
   MasterWin.Deinit();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- call OnTick event handler of main module
   MasterWin.OnEventTick();
  }
//+------------------------------------------------------------------+
//| Expert Event function                                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- call OnChartEvent handler of main module
   MasterWin.OnEvent(id,lparam,dparam,sparam);
  }

Dies ist der abgeschlossene Code des Expert Advisors. Während des gesamten Projekts müssen keinerlei zusätzliche Veränderungen gemacht werden!

2) Hauptmodul = Klasse

Alle Haupt- und Hilfsmodule des Projekts beginnen mit ihrer Entwicklung hier. Dieses Verfahren vereinfacht das Programmieren komplexer multi-modularer Projekte und erleichtert die Suche nach möglichen Fehlern. Sie zu finden ist jedoch ziemlich schwer. Manchmal ist es in der Tat leichter und schneller, ein neues Projekt komplett zu schreiben als nach diesen schwer fassbaren "Fehlern" zu suchen.

//+------------------------------------------------------------------+
//|                                           ClassMasterWindows.mqh |
//|                                                 Copyright DC2008 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "DC2008"
#property link      "http://www.mql5.com"
//+------------------------------------------------------------------+
//| Main module: CMasterWindows class                                |
//+------------------------------------------------------------------+
class CMasterWindows
  {
protected:
   bool              on_event;   // event processing flag
public:
   // Class constructor
   void              CMasterWindows();
   // Method of launching the main module (core algorithm)
   void              Run();
   // Deinitialization method
   void              Deinit();
   // OnTick event processing method
   void              OnEventTick();
   // OnChartEvent event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Unten finden Sie eine grobe erste Beschreibung der Hauptmethoden der Klasse.

//+------------------------------------------------------------------+
//| CMasterWindows class constructor                                 |
//+------------------------------------------------------------------+
void CMasterWindows::CMasterWindows()
   {
//--- class members initialization
   on_event=false;   // disable events processing
   }
//+------------------------------------------------------------------+
//| Метод запуска главного модуля (основной алгоритм)                |
//+------------------------------------------------------------------+
void CMasterWindows::Run()
   {
//--- Main functional of the class: runs additional modules
   ObjectsDeleteAll(0,0,-1);
   Comment("MasterWindows for MQL5     © DC2008");
//---
   on_event=true;   // enable events processing
   }
//+------------------------------------------------------------------+
//| Deinitialization method                                          |
//+------------------------------------------------------------------+
void CMasterWindows::Deinit()
   {
//--- 
   ObjectsDeleteAll(0,0,-1);
   Comment("");
   }
//+------------------------------------------------------------------+
//| OnTick() event processing method                                 |
//+------------------------------------------------------------------+
void CMasterWindows::OnEventTick()
   {
   if(on_event) // event processing is enabled
     {
     //---
     }
   }
//+------------------------------------------------------------------+
//| OnChartEvent() event processing method                           |
//+------------------------------------------------------------------+
void CMasterWindows::OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   if(on_event) // event processing is enabled
     {
     //---
     }
  }

3) Die Library an Basis- und abgeleiteten Klassen

Die Library kann jede Anzahl abgeleiteter Klassen enthalten und daher sollte man sie am besten in separate Dateien gruppieren, die dann zusammen mit der Basisklasse (falls es eine gibt) mit eingeschlossen werden. So ist es nämlich leichter, die notwendigen Veränderungen und Ergänzungen hinzuzufügen und eben auch - nach Fehlern zu suchen.

Und schon haben wir das Programm-Framework. Testen wir es gleich und schauen mal, ob es korrekt funktioniert - also: erstellen und ausführen. Ist der Test erfolgreich verlaufen, können wir das Programm mit zusätzlichen Modulen füllen.

Beginnen wir hier also mit de Verknüpfung von abgeleiteten Klassen und fangen bei den Zellen an:

Namensklasse
Bild
 Klasse CCellText

 Klasse CCellEdit

 Klasse CCellButton

 Klasse CCellButtonType

Tabelle 1 Library der Zellklassen

Betrachten wir uns die Erzeugung einer einzigen abgeleiteten Klasse des CCellButtonTyps im Detail. Diese Klasse erzeugt Tasten verschiedener Arten.

//+------------------------------------------------------------------+
//| CCellButtonType class                                            |
//+------------------------------------------------------------------+
class CCellButtonType:public CCell
  {
public:
   ///Class constructor
   void              CCellButtonType();
   virtual     ///Draw method
   void              Draw(string m_name,
                          int m_xdelta,
                          int m_ydelta,
                          int m_type);
  };
//+------------------------------------------------------------------+
//| CCellButtonType class constructor                                |
//+------------------------------------------------------------------+
void CCellButtonType::CCellButtonType()
  {
   type=OBJ_BUTTON;
   on_event=false;   //disable events processing
  }
//+------------------------------------------------------------------+
//| CCellButtonType class Draw method                                |
//+------------------------------------------------------------------+
void CCellButtonType::Draw(string m_name,
                           int m_xdelta,
                           int m_ydelta,
                           int m_type)
  {
//--- creating an object with specified name
   if(m_type<=0) m_type=0;
   name=m_name+".Button"+(string)m_type;
   if(ObjectCreate(0,name,type,0,0,0,0,0)==false)
      Print("Function ",__FUNCTION__," error ",GetLastError());
//--- object properties initializartion
   ObjectSetInteger(0,name,OBJPROP_COLOR,Property.TextColor);
   ObjectSetInteger(0,name,OBJPROP_BGCOLOR,Property.BGColor);
   ObjectSetInteger(0,name,OBJPROP_CORNER,Property.Corner);
   ObjectSetInteger(0,name,OBJPROP_XDISTANCE,m_xdelta);
   ObjectSetInteger(0,name,OBJPROP_YDISTANCE,m_ydelta);
   ObjectSetInteger(0,name,OBJPROP_XSIZE,Property.H);
   ObjectSetInteger(0,name,OBJPROP_YSIZE,Property.H);
   ObjectSetInteger(0,name,OBJPROP_SELECTABLE,0);
   if(m_type==0) // Hide button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(MIN_WIN));
      ObjectSetString(0,name,OBJPROP_FONT,"Webdings");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,12);
     }
   if(m_type==1) // Close button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(CLOSE_WIN));
      ObjectSetString(0,name,OBJPROP_FONT,"Wingdings 2");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
     }
   if(m_type==2) // Return button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(MAX_WIN));
      ObjectSetString(0,name,OBJPROP_FONT,"Webdings");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,12);
     }
   if(m_type==3) // Plus button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,"+");
      ObjectSetString(0,name,OBJPROP_FONT,"Arial");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,10);
     }
   if(m_type==4) // Minus button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,"-");
      ObjectSetString(0,name,OBJPROP_FONT,"Arial");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,13);
     }
   if(m_type==5) // PageUp button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(PAGE_UP));
      ObjectSetString(0,name,OBJPROP_FONT,"Wingdings 3");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
     }
   if(m_type==6) // PageDown button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,CharToString(PAGE_DOWN));
      ObjectSetString(0,name,OBJPROP_FONT,"Wingdings 3");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,8);
     }
   if(m_type>6) // empty button
     {
      ObjectSetString(0,name,OBJPROP_TEXT,"");
      ObjectSetString(0,name,OBJPROP_FONT,"Arial");
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,13);
     }
   on_event=true;   //enable events processing
  }
//+------------------------------------------------------------------+

Notwendige Erklärungen:

  • Im Klassen-Konstruktor führen wir eine Sperre bei der Verarbeitung von Ereignissen ein. Das ist nötig, um das Objekt auf seine Arbeit vorzubereiten und Störungen und Ablenkungen durch ankommende Ereignisse auszuschalten. Sind dann alle erforderlichen Arbeitsschritte abgeschlossen, werden wir diese Verarbeitung wieder gestatten, und das Objekt beginnt vollständig zu funktionieren.
  • Die Draw-Methode arbeitet mit internen Daten und empfängt externe Daten. Daher sollten die Daten zunächst auf ihre Konformität geprüft und erst dann verarbeitet werden, damit es zu keinen unerwarteten Situationen kommt. Doch in unserem Beispiel hier lassen wir diesen Test erst einmal weg. Warum? Stellen Sie sich einfach vor, das Klassenobjekt ist ein Soldat, und Soldaten müssen nicht unbedingt die Pläne ihrer Vorgesetzten kennen. Sie müssen einzig und allein klar, rasch und strikt die Befehle ihrer Vorgesetzten ausführen, anstatt die erhaltenen Befehle zu analysieren und unabhängige Entscheidungen zu treffen. Daher müssen alle externen Daten entsprechend stimmen, bevor wir mit der Arbeit an dieser Klasse beginnen.

Jetzt müssen wir die ganze Library der Zellen testen. Und dazu fügen wir den folgenden Code in das Hauptmodul ein (nur kurzfristig, jetzt für unsere Testzwecke) und starten den Expert Advisor.

//--- include file with classes
#include <ClassUnit.mqh>
//+------------------------------------------------------------------+
//| Main module: CMasterWindows class                                |
//+------------------------------------------------------------------+
class CMasterWindows
  {
protected:
   bool              on_event;   // events processing flag
   WinCell           Property;   // cell property
   CCellText         Text;
   CCellEdit         Edit;
   CCellButton       Button;
   CCellButtonType   ButtonType;
public:
   // Class constructor
   void              CMasterWindows();
   // Main module run method (core algorithm)
   void              Run();
   // Deinitialization method
   void              Deinit();
   // OnTick event processing method
   void              OnEventTick();
   // OnChart event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };
//+------------------------------------------------------------------+
//| Main module run method (core algorithm)                          |
//+------------------------------------------------------------------+
void CMasterWindows::Run()
  {
//--- core algorithm - it launches additional modules
   ObjectsDeleteAll(0,0,-1);
   Comment("MasterWindows for MQL5     © DC2008");
//--- Text field
   Text.Draw("Text",50,50,150,"Text field");
//--- Edit field
   Edit.Draw("Edit",205,50,150,"default value",true);
//--- LARGE BUTTON
   Button.Draw("Button",50,80,200,"LARGE BUTTON");
//--- Hide button
   ButtonType.Draw("type0",50,100,0);
//--- Close button
   ButtonType.Draw("type1",70,100,1);
//--- Return  button
   ButtonType.Draw("type2",90,100,2);
//--- Plus button
   ButtonType.Draw("type3",110,100,3);
//--- Minus button
   ButtonType.Draw("type4",130,100,4);
//--- None button
   ButtonType.Draw("type5",150,100,5);
//--- None button
   ButtonType.Draw("type6",170,100,6);
//--- None button
   ButtonType.Draw("type7",190,100,7);
//---
   on_event=true;   // enable events processing
  }

Und wi dürfen nicht vergessen, die Ereignisse für die daraus resultierenden Klassen zu übertragen! Ist das noch nicht geschehen, kann der Umgang mit Projekten extrem schwer oder gar unmöglich werden.

//+------------------------------------------------------------------+
//| CMasterWindows class OnChart event processing method             |
//+------------------------------------------------------------------+
void CMasterWindows::OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   if(on_event) // event processing is enabled
     {
      //--- process events for the cell class objects
      Text.OnEvent(id,lparam,dparam,sparam);
      Edit.OnEvent(id,lparam,dparam,sparam);
      Button.OnEvent(id,lparam,dparam,sparam);
      ButtonType.OnEvent(id,lparam,dparam,sparam);
     }
  }

Als Ergebnis sehen wir nun alle verfügbaren Optionen für Objekte der Library der Zellklassen.

Abb. 2 Library der Zellklassen

Abb. 2 Library der Zellklassen

Testen wir jetzt die Arbeitseffizienz und die Reaktionen der Objekte auf Ereignisse:

  • In das Bearbeitungsfeld geben wir jetzt anstatt "Standard" verschiedene Variablen ein. Variieren diese Werte, dann hat der Test geklappt.
  • Wir drücken auf die Tasten, die in gedrückten Zustand bleiben, bis sie abermals gedrückt werden. Das ist jedoch noch keine zufriedenstellende Reaktion. Die Tasten müssen, nachdem sie einmal gedrückt wurden, automatisch in ihren Ursprungszustand zurückspringen. Und jetzt können wir die Stärke des OOP zeigen - die Möglichkeit der Vererbung. Unser Programm kann mehr als ein Dutzend Tasten verwenden, und dazu muss nicht jeder Taste extra die gewünschte Funktionalität hinzugefügt werden. Es reicht aus, die CCell Basisklasse zu ändern und wie von Zauberhand beginnen alle Objekte der abgeleiteten Klassen korrekt und reibungslos zu funktionieren!
//+------------------------------------------------------------------+
//| CCell class OnChart event processing method                      |
//+------------------------------------------------------------------+
void CCell::OnEvent(const int id,
                    const long &lparam,
                    const double &dparam,
                    const string &sparam)
  {
   if(on_event) // event processing is enabled
     {
      //--- button click event
      if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,".Button",0)>0)
        {
         if(ObjectGetInteger(0,sparam,OBJPROP_STATE)==1)
           {
            //--- if button stays pressed
            Sleep(TIME_SLEEP);
            ObjectSetInteger(0,sparam,OBJPROP_STATE,0);
            ChartRedraw();
           }
        }
     }
  }

Also ist die Library der Zellklassen getestet und mit dem Projekt verknüpft.

Als nächstes müssen wir eine Library an Linien hinzufügen:

 Namensklasse
Bild
 Klasse CRowType1 (0)

 Klasse CRowType1 (1)

 Klasse CRowType1 (2)

 Klasse CRowType1 (3)

 Klasse CRowType2

 Klasse CRowType3

 Klasse CRowType4

 KlasseCRowType5

 KlasseCRowType6

Tabelle 2 Library der Linienklassen

Wir testen sie ganz genauso. Nachdem alle Test abgeschlossen sind, kommen wir zur nächsten Phase.

2.4 Phase IV: Das Projekt bauen

Bis jetzt sind alle notwendigen Module erzeugt und getestet worden. Also machen wir uns nun an den Aufbau des Projekts. Als erstes erzeugen wir eine Kaskade mit der Form des Fensters aus Abb. 1 und füllen sie mit Funktionalität, d.h. programmierten Reaktionen aller Elemente und Module auf ankommende Ereignisse.

Dazu haben wir einen bereits fertigen Programmrahmen und das bereits vorbereitete Hauptmodul. Fangen wir mit ihm an. Das Modul ist eine der "Nachfolger"-Klassen der CWin Basisklasse, sodass sie alle öffentlichen Methoden und Felder der "Vorgänger"-Klasse per Vererbung übertragen bekommen hat. Deshalb müssen wir jetzt nur ein paar Methoden außer Kraft setzen, und schon ist unsere neue CMasterWindows Klasse fertig:

//--- include files with classes
#include <ClassWin.mqh>
#include <InitMasterWindows.mqh>
#include <ClassMasterWindowsEXE.mqh>
//+------------------------------------------------------------------+
//| CMasterWindows class                                             |
//+------------------------------------------------------------------+
class CMasterWindows:public CWin
  {
protected:
   CMasterWindowsEXE WinEXE;     // executable module
public:
   void              Run();      // Run method
   void              Deinit();   // Deinitialization method
   virtual                       // OnChart event processing method
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };
//+------------------------------------------------------------------+
//| CMasterWindows class deinitialization method                     |
//+------------------------------------------------------------------+
void CMasterWindows::Deinit()
  {
//---(delete all objects)
   ObjectsDeleteAll(0,0,-1);
   Comment("");
  }
//+------------------------------------------------------------------+
//| CMasterWindows class Run method                                  |
//+------------------------------------------------------------------+
void CMasterWindows::Run()
  {
   ObjectsDeleteAll(0,0,-1);
   Comment("MasterWindows for MQL5     © DC2008");
//--- creating designer window and launch executable object
   SetWin("CWin1",1,30,250,CORNER_RIGHT_UPPER);
   Draw(Mint,Mstr,21);
   WinEXE.Init("CWinNew",30,18);
   WinEXE.Run();
  }
//+------------------------------------------------------------------+
//| CMasterWindows class event processing method                     |
//+------------------------------------------------------------------+
void CMasterWindows::OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   if(on_event) // event processing is enabled
     {
      //--- Close button click in the main window
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,"CWin1",0)>=0
         && StringFind(sparam,".Button1",0)>0)
        {
         ExpertRemove();
        }
      //--- OnChart event processing for all objects
      STR1.OnEvent(id,lparam,dparam,sparam);
      STR2.OnEvent(id,lparam,dparam,sparam);
      STR3.OnEvent(id,lparam,dparam,sparam);
      STR4.OnEvent(id,lparam,dparam,sparam);
      STR5.OnEvent(id,lparam,dparam,sparam);
      STR6.OnEvent(id,lparam,dparam,sparam);
      WinEXE.OnEvent(id,lparam,dparam,sparam);
     }
  }

Das Hauptmodul an sich ist ziemlich klein, da es ja nichts anderes tun soll, als das Anwendungsfenster zu erzeugen. Danach überträgt es die Kontrolle auf das ausführbare WinEXE Modul, wo das wirklich Interessante stattfindet - die Reaktion auf ankommende Ereignisse.

Vorhin haben wir eine einfache WinCell Struktur zum Datenaustausch zwischen Objekten erzeugt, und jetzt sehen wir plötzlich, welche Vorteile uns diese Vorgehensweise gebracht hat. Der Kopierprozess aller Mitglieder dr Struktur ist sehr logisch und kompakt:

STR1.Property = Property;
   STR2.Property = Property;
   STR3.Property = Property;
   STR4.Property = Property;
   STR5.Property = Property;
   STR6.Property = Property;

Zu diesem Zeitpunkt könne wir die Detailbetrachtung des Klassendesigns beenden und zur visuellen Technologie ihres Baus weitergehen, was den Erzeugungsprozess neuer Klassen erheblich beschleunigt.

3. Visuelles Design der Klassen

Eine Klasse kann im MasterWindows Design für MQL5 viel schneller gebaut und erheblich leichter visualisiert werden:


Abb. 3 Der Prozess des visuellen Designs

Abb. 3 Der Prozess des visuellen Designs

Alles was vom Entwickler jetzt verlangt wird, ist das Fensterformular zu zeichnen und zwar mit Hilfe des MasterWindows Formulars, und danach einfach die Reaktion auf das geplante Ereignis festzulegen. Der Code selbst wir automatisch erzeugt. Und das ist alles! Das Projekt ist abgeschlossen.

Abb. 4 zeigt ein Beispiel eines erzeugten Codes der CMasterWindows Klasse sowie des Expert Advisors (eine Datei wird im Order ...\MQL5\Files angelegt):

//****** Project (Expert Advisor): project1.mq5
//+------------------------------------------------------------------+
//|        Code has been generated by MasterWindows Copyright DC2008 |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "DC2008"
//--- include files with classes
#include <ClassWin.mqh>
int Mint[][3]=
  {
     {1,0,0},
     {2,100,0},
     {1,100,0},
     {3,100,0},
     {4,100,0},
     {5,100,0},
     {6,100,50},
     {}
  };
string Mstr[][3]=
  {
     {"New window","",""},
     {"NEW1","new1",""},
     {"NEW2","new2",""},
     {"NEW3","new3",""},
     {"NEW4","new4",""},
     {"NEW5","new5",""},
     {"NEW6","new6",""},
     {}
  };
//+------------------------------------------------------------------+
//| CMasterWindows class (main unit)                                 |
//+------------------------------------------------------------------+
class CMasterWindows:public CWin
  {
private:
   long              Y_hide;          // Window shift vertical in hide mode
   long              Y_obj;           // Window shift vertical
   long              H_obj;           // Window shift horizontal
public:
   bool              on_hide;         // HIDE mode flag
   CArrayString      units;           // Main window lines
   void              CMasterWindows() {on_event=false; on_hide=false;}
   void              Run();           // Run method
   void              Hide();          // Hide method
   void              Deinit()         {ObjectsDeleteAll(0,0,-1); Comment("");}
   virtual void      OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };
//+------------------------------------------------------------------+
//| CMasterWindows class Run method                                  |
//+------------------------------------------------------------------+
void CMasterWindows::Run()
  {
   ObjectsDeleteAll(0,0,-1);
   Comment("Code has been generated by MasterWindows for MQL5 © DC2008");
//--- creating main window and launch executable module
   SetWin("project1.Exp",50,100,250,CORNER_LEFT_UPPER);
   Draw(Mint,Mstr,7);
  }
//+------------------------------------------------------------------+
//| CMasterWindows class Hide method                                 |
//+------------------------------------------------------------------+
void CMasterWindows::Hide()
  {
   Y_obj=w_ydelta;
   H_obj=Property.H;
   Y_hide=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS,0)-Y_obj-H_obj;;
//---
   if(on_hide==false)
     {
      int n_str=units.Total();
      for(int i=0; i<n_str; i++)
        {
         long y_obj=ObjectGetInteger(0,units.At(i),OBJPROP_YDISTANCE);
         ObjectSetInteger(0,units.At(i),OBJPROP_YDISTANCE,(int)y_obj+(int)Y_hide);
         if(StringFind(units.At(i),".Button0",0)>0)
            ObjectSetString(0,units.At(i),OBJPROP_TEXT,CharToString(MAX_WIN));
        }
     }
   else
     {
      int n_str=units.Total();
      for(int i=0; i<n_str; i++)
        {
         long y_obj=ObjectGetInteger(0,units.At(i),OBJPROP_YDISTANCE);
         ObjectSetInteger(0,units.At(i),OBJPROP_YDISTANCE,(int)y_obj-(int)Y_hide);
         if(StringFind(units.At(i),".Button0",0)>0)
            ObjectSetString(0,units.At(i),OBJPROP_TEXT,CharToString(MIN_WIN));
        }
     }
//---
   ChartRedraw();
   on_hide=!on_hide;
  }
//+------------------------------------------------------------------+
//| CMasterWindows class OnChartEvent event processing method        |
//+------------------------------------------------------------------+
void CMasterWindows::OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   if(on_event // event handling is enabled
      && StringFind(sparam,"project1.Exp",0)>=0)
     {
      //--- call of OnChartEvent handlers
      STR1.OnEvent(id,lparam,dparam,sparam);
      STR2.OnEvent(id,lparam,dparam,sparam);
      STR3.OnEvent(id,lparam,dparam,sparam);
      STR4.OnEvent(id,lparam,dparam,sparam);
      STR5.OnEvent(id,lparam,dparam,sparam);
      STR6.OnEvent(id,lparam,dparam,sparam);
      //--- creating graphic object
      if(id==CHARTEVENT_OBJECT_CREATE)
        {
         if(StringFind(sparam,"project1.Exp",0)>=0) units.Add(sparam);
        }
      //--- edit [NEW1] in Edit STR1
      if(id==CHARTEVENT_OBJECT_ENDEDIT
         && StringFind(sparam,".STR1",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW3] : Plus button STR3
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR3",0)>0
         && StringFind(sparam,".Button3",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW3] : Minus button STR3
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR3",0)>0
         && StringFind(sparam,".Button4",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW4] : Plus button STR4
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR4",0)>0
         && StringFind(sparam,".Button3",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW4] : Minus button STR4
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR4",0)>0
         && StringFind(sparam,".Button4",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW4] : Up button STR4
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR4",0)>0
         && StringFind(sparam,".Button5",0)>0)
        {
        //--- event processing code
        }
      //--- edit [NEW4] : Down button STR4
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR4",0)>0
         && StringFind(sparam,".Button6",0)>0)
        {
        //--- event processing code
        }
      //--- [new5] button click STR5
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR5",0)>0
         && StringFind(sparam,".Button",0)>0)
        {
        //--- event processing code
        }
      //--- [NEW6] button click STR6
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR6",0)>0
         && StringFind(sparam,"(1)",0)>0)
        {
        //--- event processing code
        }
      //--- [new6] button click STR6
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR6",0)>0
         && StringFind(sparam,"(2)",0)>0)
        {
        //--- event processing code
        }
      //--- button click [] STR6
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".STR6",0)>0
         && StringFind(sparam,"(3)",0)>0)
        {
        //--- event processing code
        }
      //--- Close button click in the main window
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".Button1",0)>0)
        {
         ExpertRemove();
        }
      //--- Hide button click in the main window
      if(id==CHARTEVENT_OBJECT_CLICK
         && StringFind(sparam,".Button0",0)>0)
        {
         Hide();
        }
     }
  }
//--- Main module declaration
CMasterWindows MasterWin;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- launch main module
   MasterWin.Run();
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- main module deinitialization
   MasterWin.Deinit();
  }
//+------------------------------------------------------------------+
//| Expert Event function                                            |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- call OnChartEvent event handler
   MasterWin.OnEvent(id,lparam,dparam,sparam);
  }

Beim Starten sehen wir dann das folgende entwickelte Fester:

Abb. 4 Advisor Projekt1 - Ergebnis des visuellen Designs von Klassen

Abb. 4 Expert Advisor Projekt1 - Ergebnis des visuellen Designs von Klassen

Fazit

  1. Klassen müssen Schritt für Schritt in verschiedenen Phasen entwickelt werden. Durch Herunterbrechen der Gesamtaufgabe in Module, kann für jedes Modul eine separate Klasse erzeugt werden. Die Module werden ihrerseits wiederum in Kleinstmodule abgeleiteter oder Basisklassen heruntergebrochen.
  2. Darauf achten, die Basisklassen nicht mit eingebauten Methoden zu überfrachten - die Anzahl der Methoden sollte auf ein Minimum beschränkt sein.
  3. Das Design von Klassen mit Hilfe der visuellen Design-Umgebung ist sehr einfach, selbst für einen blutigen Anfänger, da der Code automatisch erzeugt wird.

Standort der Anhänge:

  • masterwindows.mq5 - ...\MQL5\Experts\
  • Rest im Ordner - ...\MQL5\Include\

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/53

Beigefügte Dateien |
Erzeugung eines Indikators mit mehreren Indikator-Buffern für Anfänger Erzeugung eines Indikators mit mehreren Indikator-Buffern für Anfänger
Die komplexen Codes bestehen aus einer Reihe einfacher Codes. Kennt man diese, dann sieht alles gleich nicht mehr so kompliziert aus. In diesem Beitrag beschäftigen wir uns mit der Erzeugung eines Indikators mit mehreren Indikator-Buffern. Als Beispiel wird hierzu der Aroon-Indikator detailliert analysiert und zwei unterschiedliche Versionen dieses Codes präsentiert.
Genetische Algorithmen - Leicht gemacht! Genetische Algorithmen - Leicht gemacht!
Der Verfasser behandelt in diesem Beitrag evolution&auml;re Berechnungen mit Hilfe eines pers&ouml;nlich entwickelten, genetischen Algorithmus. Er zeigt die Funktionsweise dieses Algorithmus anhand von Beispielen und gibt praktische Empfehlungen f&uuml;r seine Verwendung.
Die Interaktion von MetaTrader 5 und MATLAB Die Interaktion von MetaTrader 5 und MATLAB
Dieser Beitrag beschäftigt sich mit den dEtailös der Interaktion zwischen MetaTrader 5 und dem MatLab Mathematik-Paket. Er erklärt die Mechanismen der Datenkonvertierung, den Entwicklungsprozess einer universellen Library, die mit dem MATLAB-Desktop interagieren kann. Zudem wird auch die Verwendung von DLL erklärt, die durch die MatLab Umgebung erzeugt werden. Dieser Beitrag richtet sich an bereits erfahrene Leser, die C++ und MQL5 kennen.
Exportieren von Angeboten aus MetaTrader 5 in .NET-Anwendungen mithilfe von WCF-Services Exportieren von Angeboten aus MetaTrader 5 in .NET-Anwendungen mithilfe von WCF-Services
Möchten Sie den Export von Angeboten aus MetaTrader 5 in Ihre eigene Anwendung aktualisieren? Mit der MQL5-DLL-Verbindung können Sie solche Lösungen erschaffen! Dieser Beitrag zeigt Ihnen eine der Möglichkeiten zum Exportieren von Angeboten aus MetaTrader 5 in .NET-Anwendungen. Für mich war es interessanter, sinnvoller und einfacher, den Export von Angeboten mithilfe dieser Plattform umzusetzen. Leider wird .NET von Version 5 weiterhin nicht unterstützt, sodass wir wie in den alten Tagen win32 dll mit .NET-Unterstützung als Zwischenlösung nutzen werden.