English Русский Español 日本語 Português
preview
Entwicklung eines Replay Systems (Teil 33): Auftragssystem (II)

Entwicklung eines Replay Systems (Teil 33): Auftragssystem (II)

MetaTrader 5Beispiele | 10 Mai 2024, 10:30
92 0
Daniel Jose
Daniel Jose

Einführung

Im vorherigen Artikel „Entwicklung eines Replay Systems (Teil 32): Auftragssystem (I)“ haben wir begonnen, ein Auftragssystem zu entwickeln, das im Expert Advisor verwendet werden kann. Wir haben eine Basissystemklasse entwickelt, die nur die Funktionen enthält, die wir zu Beginn wirklich brauchen.

Man könnte meinen, und das ist nicht ganz falsch, dass diese Funktionen im Grunde sehr grundlegend sind und das Auftragssystem nicht vollständig abdecken können. Das ist richtig, aber in diesem frühen Stadium werden wir ein System entwickeln, das nicht direkt im Replay/Simulationsdienst verwendet wird. Wir werden ein System entwickeln, das mit einem echten Handelsserver arbeiten kann, sowohl auf einem Demokonto als auch auf einem echten Konto. Wir werden die Plattform MetaTrader 5 ausgiebig nutzen, die uns von Anfang an alle notwendige Unterstützung bietet. Es ist sehr wichtig, dass das System von Anfang an einwandfrei funktioniert, wenn die Plattform mit dem Server kommuniziert. Denn wenn nur Replay/Simulator verwendet wird, können wir das Expert Advisor-System nicht mehr an unseren Replay/Simulator anpassen. Wir müssen sicherstellen, dass die Wiedergabe/der Simulator dem entspricht, was der EA tut. Aus diesem Grund müssen wir den EA zunächst auf einem echten Server ausführen.

Diese Anforderung führt zu einer reduzierten Anzahl von Methoden und Funktionen im System und damit in der Klasse C_Orders. Wir dürfen jedoch nicht vergessen, dass wir ein großes System schaffen. Machen Sie keinen Fehler, wenn Sie denken, dass die Klasse C_Orders nicht in der Lage sein wird, alle unsere Bedürfnisse zu erfüllen. Wir müssen nur dafür sorgen, dass die MetaTrader 5-Plattform uns die maximale Unterstützung bietet, die für die Arbeit mit dem Handelsserver erforderlich ist. Das Wesen des maximalen Supports von MetaTrader 5 besteht darin, Dinge zu vermeiden, wo solche Dinge vermieden werden können. Wir sollten nicht versuchen, das Rad neu zu erfinden, sondern die Plattform so gut wie möglich nutzen, um den geringsten Aufwand bei der Arbeit mit dem Replay/Simulator-System zu haben.

Bevor ich jedoch mit dem Auftragssystem fortfahre, möchte ich Ihnen zeigen, wie Sie ein Problem lösen können. Dies ist jedoch kein Problem, sondern eher eine Unannehmlichkeit, die in MetaTrader 5 vorhanden ist.


Der Versuch, die Dinge zu vereinfachen

Wenn Sie MetaTrader 5 regelmäßig nutzen, haben Sie wahrscheinlich eine Sache bemerkt, die einerseits ärgerlich, andererseits aber auch entmutigend ist, um es vorsichtig auszudrücken. Was ich meine, wird vor allem von Personen wahrgenommen, die von anderen Plattformen auf MetaTrader 5 umgestiegen sind und sich nach der Begegnung mit dieser Funktion in einigen Aspekten, die vor allem die Chartanalyse betreffen, irritiert fühlen oder zögern, sie zu nutzen.

Das Problem besteht darin, dass bei der Analyse des Charts des Finanzinstruments manchmal Schwierigkeiten beim Ändern oder Löschen der für die Analyse verwendeten grafischen Objekte auftreten können. Es gibt einige Dinge, die Sie in MetaTrader 5 konfigurieren müssen, um wirklich zu wissen, wie man damit umgeht. Nehmen wir an, Sie haben die Plattform gerade erst auf Ihrem Computer installiert und noch nie mit ihr gearbeitet. Dies ist also Ihr erster Kontakt mit der Plattform. Ich möchte eine Sache hervorheben: Ich spreche von den ersten Erfahrungen, die jemand mit MetaTrader 5 gemacht hat.

Das ist eine interessante Frage. Wir können durch einen Doppelklick auf alte Objekte zugreifen. Solange kein Flag gesetzt ist, das die Auswahl deaktiviert (Kontrollkästchen, siehe Abb. 01), können wir tatsächlich auf das Objekt zugreifen.

Abbildung 01

Abbildung 01 - Kontrollkästchen aktiviert.

Das ist jedoch nicht der Punkt. Das Problem ist, dass viele Nutzer, die von anderen Plattformen kommen, daran gewöhnt sind, nur auf ein Objekt zu klicken. Das Objekt ist damit ausgewählt und kann geändert oder aus dem Chart entfernt werden. Es ist nicht ganz offensichtlich, dass MetaTrader 5 einen Doppelklick erfordert, und es dauert einige Zeit, bis man herausfindet, wie man in solchen Fällen vorgehen muss.

Lassen Sie uns nun über die Details sprechen. Im MetaTrader 5 ist es möglich, dieses Verhalten zu ändern. Um auf die Einstellungen zuzugreifen, drücken Sie CTRL + O. Daraufhin öffnet sich das MetaTrader 5 Optionsfenster. Gehen Sie in diesem Fenster auf die Registerkarte Chart und aktivieren Sie sie: „Objekt mit einem Mausklick auswählen“. Auf diese Weise können Sie die Auswahl in MetaTrader 5 wie in anderen Plattformen verwenden. Aber selbst in diesem Fall, mit dieser kleinen Anpassung, werden wir immer noch einige Funktionen vermissen. Ich möchte hier zeigen, wie man es anders und mit neuen Möglichkeiten machen kann.

Als Programmierer befand ich mich in einer Situation, in der ich irgendwie sicherstellen kann, dass der Nutzer, der zu MetaTrader 5 wechselt, die richtige und angenehme Erfahrung bei der Arbeit mit der Plattform macht. Und das wird so umgesetzt, dass Sie nur einmal drücken müssen. Wenn der Nutzer jedoch nicht über genügend Programmiererfahrung verfügt, ist die Lösung möglicherweise nicht so offensichtlich. Außerdem könnten manche Menschen diese Arbeit für unnötig halten. Dies wird jedoch unsere Möglichkeiten erweitern.

Um dieses Problem zu lösen, kehren wir zur Klasse C_Terminal zurück und fügen ihr ein paar Zeilen Code hinzu. Dies wird ausreichen, um neuen Nutzern eine einfachere Erfahrung zu ermöglichen. Schauen wir uns an, was hinzugefügt werden sollte. Siehe den nachstehenden Code:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      static string st_str = "";
                                
      switch (id)
      {
         case CHARTEVENT_CHART_CHANGE:
            m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
            m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
            break;
         case CHARTEVENT_OBJECT_CLICK:
            if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
            if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
            ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
            break;
         case CHARTEVENT_OBJECT_CREATE:
            if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
            st_str = sparam;
            break;
      }
   }

Diese Methode ist bereits Teil des ursprünglichen Codes für die Klasse C_Terminal. Sie enthält jedoch einige zusätzliche Zeilen, die sich auf zwei neue Ereignisse beziehen. Sie stammen von der Plattform MetaTrader 5. Es gibt ein Detail im obigen Code. Aber zuerst werde ich den Code erklären und dann die Details erläutern. Wir deklarieren eine statische, lokale Variable, die dazu dient, den Namen des zu untersuchenden Objekts zu speichern. Diese Variable wird mit einer leeren Zeichenkette initialisiert. Das ist sehr wichtig, wenn wir wollen, dass alles richtig gemacht wird. Wenn wir auf ein Objekt im Chart klicken, wird sein Name an unser Programm übergeben. Außerdem übergibt die Plattform das Ereignis CHARTEVENT_OBJECT_CLICK, wobei der Objektname in der Variablen sparam steht. Diese kleinen Details werden uns im nächsten Schritt helfen. Überprüfen wir nun, ob der Name in unserer lokalen Variable mit dem von MetaTrader 5 gemeldeten übereinstimmt. Im Falle der ersten Ausführung sind sie unterschiedlich; wenn sie gleich sind, passiert nichts. Wenn sie jedoch unterschiedlich sind, wird das Objekt, das sich im Fokus der Plattform befand, zum Fokus. Dies geschieht in der Zeile, in der wir den Fokus des Objekts überprüfen und gegebenenfalls entfernen.

Beachten Sie nun bitte den folgenden Punkt: Wenn Objekteigenschaften so geändert werden, dass sie wie in Abbildung 01 aussehen, müssen wir verstehen, dass der Nutzer bewusst sagt, dass dieses Objekt nicht versehentlich Aufmerksamkeit erhalten soll. Entweder, weil das Objekt nicht verändert werden soll, oder weil es auf etwas zeigen wird und wir seine Position oder etwas Ähnliches nicht verändern wollen. Wenn wir also das Kontrollkästchen wie in Abb. 01 markieren, müssen wir verstehen, dass das Objekt ignoriert wird. Das ist genau das, was diese Prüfung bewirkt. Sie prüft, ob das angegebene Objekt ignoriert werden soll. Ohne dieses Häkchen würde das vom Nutzer in der Objekteigenschaft markierte Kontrollkästchen (wie in Abbildung 01 dargestellt) ignoriert und das Objekt wäre zugänglich. Um einem Objekt diesen Fokus zu geben, verwenden wir also diesen speziellen Code. Beachten Sie, dass wir in diesem Code den Namen des Objekts speichern, damit unser Programm weiß, welches Objekt es war, wenn wir den Fokus verlieren. Etwas Ähnliches passiert, wenn MetaTrader 5 das Ereignis CHARTEVENT_OBJECT_CREATE auslöst. Allerdings gibt es hier ein Detail. Im Gegensatz zum CHARTEVENT_OBJECT_CLICK-Ereignis tritt dieses Objekt-Erzeugungsereignis in unserem Programm nur dann auf, wenn wir MetaTrader 5 mitteilen, dass wir über die Objekt-Erzeugung informiert werden wollen.

Anmerkung: In jedem Fall löst MetaTrader 5 immer Ereignisse aus. Einige von ihnen werden jedoch nur dann zu unserem Code geleitet, wenn wir die Plattform darüber informieren.

Um MetaTrader 5 mitzuteilen, dass wir benachrichtigt werden möchten, wenn ein Objekt auf dem Chart erstellt wird, gehen wir genauso vor wie beim Löschen eines Objekts. Hier ist der Code:

C_Terminal()
   {
      m_Infos.ID = ChartID();
      CurrentSymbol();
      m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
      m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
      m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
      m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
      m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
      m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
      m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
      m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
      m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
      m_Infos.ChartMode     = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
      ResetLastError();
   }

Diese Zeile teilt MetaTrader 5 mit, dass wir von nun an Benachrichtigungen erhalten möchten, wenn ein neues Objekt im Chart erstellt wird. In ähnlicher Weise müssen wir MetaTrader 5 mitteilen, wann wir keine Benachrichtigungen mehr erhalten möchten. Dies geschieht mit Hilfe des folgenden Codes:

~C_Terminal()
   {
      ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, false);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false);
   }

Sobald diese Zeile ausgeführt wird, benachrichtigt MetaTrader 5 unseren Code nicht mehr über Ereignisse zur Objekterstellung. Diese Art von Dingen ist ziemlich wichtig und interessant, weil wir manchmal wissen wollen, wann der Nutzer Objekte dem Chart hinzufügt oder davon entfernt. Und der Versuch, dies „manuell“ zu tun, ist ziemlich schwierig. Mit dem MetaTrader 5 ist dies jedoch recht einfach möglich. Sie haben wahrscheinlich bemerkt, dass wir die Plattform so bearbeiten können, dass sie sich so verhält, wie wir es erwarten. Dieses Verhalten und die Tatsache, dass wir dazu in der Lage sind, bedeutet, dass wir neuen Nutzern zu einer besseren Erfahrung mit der Plattform verhelfen können, was sehr motivierend ist. Das alles ist eine Frage der Anpassung, aber die Anpassung kann schneller gehen, wenn Sie als Programmierer den „Nicht-Programmierern“ helfen können.

Kehren wir nun zu dem zurück, was wir in diesem Artikel eigentlich umsetzen wollten. Wir haben noch viel zu tun, bevor wir mit etwas anderem beginnen können.


Ausweitung des Auftragssystems

Der Titel dieses Themas ist vielleicht ein wenig übertrieben, aber ich möchte etwas zu unserem Auftragssystem beitragen, das in einer anderen Artikelserie gezeigt wurde. Sie können also die gleichen Konzepte und Kenntnisse nutzen, die wir dort erworben haben. Der letzte Artikel dieser Reihe ist unter diesem Link zu finden: „Erstellen eines automatisch arbeitenden EA (Teil 15): Automatisierung (VII)“. In diesem Artikel haben wir getestet, wie man einen EA, der manuell erstellt und verwaltet wurde, in einen automatisierten EA umwandelt. Ich spreche dieses Thema jetzt an, weil Sie vielleicht daran interessiert sind, die Funktionsweise des EA im Replay/Simulator zu beobachten, um einige Anpassungen daran vornehmen zu können. Denken Sie daran, dass die MetaTrader 5 Plattform einen Strategie-Tester anbietet, der hervorragende Arbeit leistet, um die Funktionalität des EA zu testen. Hier schaffen wir nicht etwas parallel zum Tester. Die Idee ist, eine bestimmte Technik zu üben, auch wenn der Markt geschlossen ist.

Lassen Sie uns nun die Konzepte und Ideen, die in der Serie zur Erstellung eines automatisierten EA behandelt wurden, zusammenführen. Wir beginnen mit der Timer-Klasse.

Wichtiger Hinweis: In dieser ersten Phase, in der wir den EA verwenden und ihn so entwickeln, dass er sich mehr auf die Erleichterung des Zugangs zum Handelsserver konzentriert, werden wir einen kurzen Überblick über die Funktionen haben, die wir aus dieser Artikelserie importieren werden. Für eine detailliertere und ausführlichere Erklärung lesen Sie bitte diese Serie, da sie bei einigen Fragen sehr hilfreich sein kann.


Zeitsteuerung mit C_ControlOfTime

C_ControlOfTime ist eine sehr interessante Klasse. Trotz des eher kompakten Codes erlaubt es uns auf recht einfache Weise, einen EA mit einem gewissen Maß an Kontrolle zu erstellen, der auf der Zeit basiert, zu der wir handeln können. Viele Händler machen sich oft Sorgen über die ersten Marktbewegungen und gehen daher vorzeitig in Positionen ein oder verlassen sie. Wenn wir Entscheidungen auf der Grundlage von Emotionen treffen, wo wir keine Emotionen brauchen, erleiden wir oft Verluste. Aber wenn wir uns an den Plan gehalten hätten, hätten wir zu diesem Zeitpunkt nicht gehandelt. Eine der einfachsten Möglichkeiten, diese Kontrolle zu ermöglichen, ist die Verwendung eines Zeitplans. In der Serie zur Erstellung eines automatisierten EA habe ich gezeigt, wie man einen solchen Scheduler implementiert. Importieren wir sie also in unseren neuen EA. Wir werden das gleiche Verhalten erzeugen, was sehr gut ist, weil niemand außerhalb der Zeitzone handeln will, die er für angemessen und ein wenig sicherer für den Handel auf dem Markt hält. Wir werden also die EA dazu bringen, uns dabei zu helfen.

Die Klasse C_ControlOfTime beginnt wie folgt:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Orders.mqh"
//+------------------------------------------------------------------+
class C_ControlOfTime : protected C_Orders
{
   private :
      struct st_00
      {
         datetime Init,
                  End;
      }m_InfoCtrl[SATURDAY + 1];
//+------------------------------------------------------------------+

Hier wird in einer Variablen die Zeit gespeichert, die für Operationen zur Verfügung steht. Der vielleicht merkwürdigste Teil dieses Codes ist die Verwendung der Definition von SATURDAY. Für diejenigen, die die Artikelserie über die Erstellung eines automatisierten EA nicht gelesen haben, werde ich kurz erklären, was diese Definition bedeutet. Mit dieser Definition wollen wir sagen, dass unsere Matrix alle Tage der Woche abdeckt. Aber es gibt ein wichtiges Detail: Wir müssen dem Wert eine Eins hinzufügen. Dies ist notwendig, um sicherzustellen, dass die Matrix die richtige Länge hat, da sie bei NULL beginnt. Eine Matrix mit Nullelementen kann es aber nicht geben. Wir sollten immer mit einem Wert beginnen, der größer als Null ist. Deshalb haben wir eine hinzugefügt. Wenn wir das nicht täten, hätten wir eine Matrix mit 6 Elementen, nicht mit 7. Noch eine Sache: Sie haben wahrscheinlich bemerkt, dass die Klasse C_ControlOfTime von der Klasse C_Orders erbt. So erweitern wir die Möglichkeiten der Klasse C_Orders. Diese Erweiterung wird jedoch nicht tatsächlich auf den EA übertragen, wie Sie in den nachfolgenden Erläuterungen sehen werden. Der Grund dafür lässt sich anhand der Artikelserie über den automatisierten EA besser nachvollziehen.

Der nächste Teil des Codes ist der Klassenkonstruktor. Ihr Code ist wie folgt:

C_ControlOfTime(C_Terminal *arg, const ulong magic)
                :C_Orders(arg, magic)
   {
      for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++) ZeroMemory(m_InfoCtrl[c0]);
   }

Hier müssen Sie zwei einfache Dinge verstehen. Die erste ist, dass wir die Klasse C_Orders initialisieren, bevor der Code im Konstruktor C_ControlOfTime ausgeführt wird. Das zweite und vielleicht noch merkwürdigere Merkmal ist das Vorhandensein einer Schleife. Interessant ist die Art und Weise, wie es funktioniert. Bitte beachten Sie, dass wir in dieser Schleife eine Enumeration verwenden, die mit SONNTAG beginnt, und die Variable bis zum SAMSTAG aufsteigt. Ist es möglich? Ja, wir können es tun, aber wir müssen vorsichtig sein. Das Hauptanliegen ist, dass der SONNTAG einen niedrigeren Wert als der SAMSTAG haben sollte. Wenn dies nicht der Fall ist, werden wir Probleme beim Zugriff auf die Matrixdaten haben. Glücklicherweise beginnt die Enumeration mit SONNTAG und geht Tag für Tag bis SAMSTAG weiter, sodass wir die Wochentage erhalten.

Der Hauptvorteil dieses Programmiermodus ist, dass er das Niveau unseres Codes verbessert und die MQL5-Sprache der natürlichen Sprache sehr nahe bringt. Dieser Ansatz wurde in einer Reihe von Artikeln über den automatisierten EA verwendet und detailliert erläutert. Nun, hier habe ich nur ein paar Dinge gezeigt. Lesen Sie die vorangegangenen Artikel, um zu erfahren, wie Sie Ihren Code noch besser lesbar machen können. Dadurch wird Ihr Code auch für diejenigen verständlich, die nicht über ausreichende Programmierkenntnisse verfügen. Vergessen Sie nicht: Sie programmieren nicht für die Maschine, Sie programmieren für andere Programmierer.

Schauen wir uns nun zwei andere Methoden an.

virtual void SetInfoCtrl(const ENUM_DAY_OF_WEEK index, const string szArg) final
   {
      string szRes[], sz1[];
      bool bLocal;
                                
      if (_LastError != ERR_SUCCESS) return;
      if ((index > SATURDAY) || (index < SUNDAY)) return;
      if (bLocal = (StringSplit(szArg, '-', szRes) == 2))
      {
         m_InfoCtrl[index].Init = (StringToTime(szRes[0]) % 86400);
         m_InfoCtrl[index].End = (StringToTime(szRes[1]) % 86400);
         bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End);
         for (char c0 = 0; (c0 <= 1) && (bLocal); c0++)
            if (bLocal = (StringSplit(szRes[0], ':', sz1) == 2))
               bLocal = (StringToInteger(sz1[0]) <= 23) && (StringToInteger(sz1[1]) <= 59);
         if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError();
      }
      if ((_LastError != ERR_SUCCESS) || (!bLocal))
      {
         Print("Error in the declaration of the time of day: ", EnumToString(index));
         ExpertRemove();
      }
   }

Dieser Teil wurde in dem Artikel „Erstellen eines automatisch arbeitenden EA (Teil 10): Automation (II)“ beschrieben und diskutiert, wo ich ausführlich erklärt habe, wie es funktioniert. Wenn Sie mehr Details dazu benötigen oder den Code nicht verstehen, lesen Sie den erwähnten Artikel, da er sehr hilfreich sein wird. Die nächste Methode (siehe unten) wurde in demselben Artikel beschrieben. Die gesamte Arbeit, die hier im Zusammenhang mit der Klasse C_ControlOfTime geleistet wurde, kann in dem oben genannten Artikel nachgelesen werden. Der einzige Unterschied liegt im Konstruktor. Das liegt daran, was wir als Nächstes tun werden.

virtual const bool CtrlTimeIsPassed(void) final
   {
      datetime dt;
      MqlDateTime mdt;
                                
      TimeCurrent(mdt);
      dt = (mdt.hour * 3600) + (mdt.min * 60);
      return ((m_InfoCtrl[mdt.day_of_week].Init <= dt) && (m_InfoCtrl[mdt.day_of_week].End >= dt));
   }

Wenn Sie den oben genannten Artikel gelesen haben, können Sie verstehen, dass der EA einige zusätzliche Parameter erhält, damit der Nutzer die Klasse C_ControlOfTime konfigurieren kann. Aber wir müssen diese Funktionalität in eine neue Klasse bringen. Wie in der Artikelserie über den automatischen EA werden wir auch hier eine Proxy-Klasse verwenden. Dies ist das Thema des nächsten Abschnitts.


C_Manager: die verwaltende Klasse

C_Manager ist eigentlich eine ziemlich interessante Klasse, weil sie den größten Teil der Komplexität, die mit dem EA-Code verbunden ist, von dem EA selbst isoliert. Das heißt, der EA wird nur ein paar Zeilen Code haben, und Klassen werden alles verwalten und manipulieren. Dieser Code wird sich von dem Code unterscheiden, den wir in der Serie der automatisierten EAs gesehen haben. Wir beginnen mit etwas Grundlegendem. Das Hauptproblem besteht darin, dass der EA nicht in allen Situationen funktionieren wird. Zunächst werden wir uns darauf konzentrieren, den EA-Betrieb so sicher wie möglich zu machen. Denn auf der Ebene müssen wir es auch in einem Replay/Simulator-System laufen lassen. Und dies ist der schwierigste Teil des Systems.

Um zu verhindern, dass die Dinge extrem kompliziert werden, werden wir den EA zunächst mit den in der MetaTrader 5-Plattform verfügbaren Mitteln arbeiten lassen. Das bedeutet, dass das Auftragssystem nicht wirklich breit gefächert ist. Er kann z.B. nicht im CrossOrder-Modus verwendet werden, d.h. in diesem frühen Stadium können wir das Ordersystem nicht verwenden, um Orders für einen anderen Vermögenswert als den zu senden, mit dem der EA arbeitet.

Sehen wir uns nun den Code für die Klasse C_Manager in ihrem derzeitigen Entwicklungsstand an. Der Code beginnt mit den folgenden Zeilen:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_ControlOfTime.mqh"
#include "..\System EA\Auxiliar\Study\C_Study.mqh"
//+------------------------------------------------------------------+
#define def_Prefix "Manager"
#define def_LINE_PRICE  def_Prefix + "_PRICE"
#define def_LINE_TAKE   def_Prefix + "_TAKE"
#define def_LINE_STOP   def_Prefix + "_STOP"
//+------------------------------------------------------------------+
#define def_AcessTerminal       (*Terminal)
#define def_InfoTerminal        def_AcessTerminal.GetInfoTerminal()
#define def_AcessMouse          (*Study)
#define def_InfoMouse           def_AcessMouse.GetInfoMouse()
//+------------------------------------------------------------------+

Hier stellen wir einige Erklärungen zur Verfügung, um die weitere Kodierung zu erleichtern. Höchstwahrscheinlich werden diese Definitionen in Zukunft noch geändert werden, da das System recht komplex ist.

Schauen wir mal, wo der Kurs beginnt. Hier ist er codiert:

class C_Manager : public C_ControlOfTime
{
   private :
      struct st00
      {
         double  FinanceStop,
                 FinanceTake;
         uint    Leverage;
         bool    IsDayTrade,
                 AccountHedging;                                         
      }m_Infos;
      struct st01
      {
         color   corPrice,
                 corTake,
                 corStop;
         bool    bCreate;
      }m_Objects;             
//+------------------------------------------------------------------+
      C_Terminal *Terminal;
      C_Study    *Study;

Hier deklarieren wir private globale Variablen der Klasse. Zur besseren Organisation habe ich die Elemente in Strukturen unterteilt. Mit diesen Strukturen wird es einfacher sein, Teile des Codes später zu ändern oder zu entfernen. Dies wird auf jeden Fall in künftigen Artikeln geschehen. Zunächst werden wir versuchen, das System in seiner einfachsten Form zum Laufen zu bringen und dabei die Möglichkeiten des MetaTrader 5 optimal zu nutzen. Ich wiederhole dies, weil Sie dieses System nicht als endgültig betrachten sollten. Es befindet sich noch in der Entwicklungsphase.

Schauen wir uns die erste Funktion in der Klasse C_Manager an. Der Code ist unten aufgeführt:

bool CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
   {
      ulong tmp;
                                
      if (!CtrlTimeIsPassed()) return false;
      tmp = C_Orders::CreateOrder(type, Price, m_Infos.FinanceStop, m_Infos.FinanceTake, m_Infos.Leverage, m_Infos.IsDayTrade);
                                
      return tmp > 0;
   }

Die Funktion sendet einen schwebenden Auftrag an den Handelsserver. Beachten Sie jedoch, dass die Aufträge nur versandt werden kann, wenn das Zeiterfassungssystem dies zulässt. Liegt die Uhrzeit außerhalb der im Zeitplan angegebenen Handelszeiten, wird der Auftrag nicht versandt. Hier muss nur der Preis angegeben werden, zu dem der Auftrag erteilt werden soll. Unabhängig davon, ob wir kaufen oder verkaufen, wird alles andere entsprechend den globalen Klassenvariablen ausgefüllt.

Nachfolgend finden Sie eine weitere Funktion, die für die Klasse privat ist:

bool ToMarket(const ENUM_ORDER_TYPE type)
   {
      ulong tmp;
                                
      if (!CtrlTimeIsPassed()) return false;
      tmp = C_Orders::ToMarket(type, m_Infos.FinanceStop, m_Infos.FinanceTake, m_Infos.Leverage, m_Infos.IsDayTrade);
                                
      return tmp > 0;
   }

Hier senden wir einen Antrag auf Ausführung zum Marktpreis, dem besten verfügbaren Preis. In diesem Fall müssen wir nur angeben, ob wir kaufen oder verkaufen wollen. Alles andere wird auf der Grundlage der Informationen aus den globalen Klassenvariablen erledigt. Sie denken jetzt vielleicht: Wie können diese globalen Variablen initialisiert werden? Sollte dies im EA-Code geschehen? Die Antwort ist NEIN. Auf globale Variablen, die in der Klasse vorhanden sind, sollte nicht ohne angemessene Sorgfalt und Kenntnis der Klasse, zu der sie gehören, zugegriffen werden.

Wir haben einige Ressourcen, um solche Variablen zu initialisieren. Für den Moment werden wir die einfachste Methode verwenden - den Konstruktor. Die Verwendung von Konstruktoren zur Initialisierung von Klassenvariablen ist zweifelsohne die beste verfügbare Methode. Aus praktischen Gründen werden wir dies jedoch später ändern. Im Moment können wir das, was wir brauchen, im Konstruktor implementieren. Ihr Code ist wie folgt:

C_Manager(C_Terminal *arg1, C_Study *arg2, color cPrice, color cStop, color cTake, const ulong magic, const double FinanceStop, const double FinanceTake, uint Leverage, bool IsDayTrade)
          :C_ControlOfTime(arg1, magic)
   {
      string szInfo = "HEDGING";
                                
      Terminal = arg1;
      Study = arg2;
      if (CheckPointer(Terminal) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
      if (CheckPointer(Study) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
      if (_LastError != ERR_SUCCESS) return;
      m_Infos.FinanceStop     = FinanceStop;
      m_Infos.FinanceTake     = FinanceTake;
      m_Infos.Leverage        = Leverage;
      m_Infos.IsDayTrade      = IsDayTrade;
      m_Infos.AccountHedging  = false;
      m_Objects.corPrice      = cPrice;
      m_Objects.corStop       = cStop;
      m_Objects.corTake       = cTake;
      m_Objects.bCreate       = false;
      switch ((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE))
      {
         case ACCOUNT_MARGIN_MODE_RETAIL_HEDGING: m_Infos.AccountHedging = true; break;
         case ACCOUNT_MARGIN_MODE_RETAIL_NETTING: szInfo = "NETTING";            break;
         case ACCOUNT_MARGIN_MODE_EXCHANGE      : szInfo = "EXCHANGE";           break;
      }
      Print("Detected Account ", szInfo);
   }

Obwohl dieser Code lang erscheint, ist er eigentlich sehr einfach, wenn man bedenkt, dass wir alle globalen Variablen in der Klasse initialisieren werden. Die gesamte Initialisierung erfolgt auf der Grundlage der vom EA-Code bereitgestellten Parameter. Also, hier initialisieren wir das System und prüfen, auf welchem Kontotyp der EA arbeitet. Dies wird später wichtig sein. Jetzt liefert sie nur noch analytische Informationen, die uns helfen, die Art des Kontos zu verstehen. Die Informationen werden in der Symbolleiste der MetaTrader 5-Plattform über eine in dieser Zeile erstellte Nachricht bereitgestellt.

Wir haben auch einen Destruktor:

~C_Manager()
   {
      ObjectsDeleteAll(def_InfoTerminal.ID, def_Prefix);
   }

Obwohl der Code diese Zeile enthält, wird sie kaum etwas bewirken. Nun, wir können nicht erwarten, dass alles reibungslos abläuft. Wenn also ein Problem auftritt, löscht der Destruktor die von der Klasse erstellten Objekte.


Schlussfolgerung

Sie haben vielleicht bemerkt, dass das Wissen wiederverwendbar ist. Wir schaffen nicht etwas aus dem Nichts. Wir sind ständig dabei, Dinge wiederzuverwenden und zu verbessern. Infolgedessen verbessern sie sich mit der Zeit. Aber die Klasse C_Manager hat noch eine Methode, die erklärt werden muss. Er befindet sich derzeit in der Entwicklungsphase, wie der EA-Code. Aber um eine angemessenere Erklärung zu geben und vor allem, um diesen Artikel nicht zu lang und mühsam zu lesen und zu verstehen, werde ich den nächsten Artikel einer Geschichte über die Methode und den EA-Code widmen.

Da der Code nicht vollständig erklärt wurde und ich nicht möchte, dass Sie irgendetwas ohne ausreichende Kenntnisse verwenden, werden in diesem Artikel keine Codes enthalten sein. Tut mir leid, aber ich halte es nicht für richtig, etwas anderes zu tun.


Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/11482

Beigefügte Dateien |
Anexo.zip (130.63 KB)
Algorithmen zur Optimierung mit Populationen: Mikro-Künstliches Immunsystem (Mikro-AIS) Algorithmen zur Optimierung mit Populationen: Mikro-Künstliches Immunsystem (Mikro-AIS)
Der Artikel befasst sich mit einer Optimierungsmethode, die auf den Prinzipien des körpereigenen Immunsystems basiert - Mikro-Künstliches Immunsystem (Micro Artificial Immune System, Micro-AIS) - eine Modifikation von AIS. Micro-AIS verwendet ein einfacheres Modell des Immunsystems und einfache Informationsverarbeitungsprozesse des Immunsystems. In dem Artikel werden auch die Vor- und Nachteile von Mikro-AIS im Vergleich zu herkömmlichen AIS erörtert.
Erstellen eines Market-Making-Algorithmus in MQL5 Erstellen eines Market-Making-Algorithmus in MQL5
Wie arbeiten die Market Maker? Betrachten wir dieses Problem und erstellen wir einen primitiven Market-Making-Algorithmus.
Entwicklung eines Replay System (Teil 34): Auftragssystem (III) Entwicklung eines Replay System (Teil 34): Auftragssystem (III)
In diesem Artikel werden wir die erste Phase der Konstruktion abschließen. Obwohl dieser Teil recht schnell erledigt ist, werde ich auf Details eingehen, die zuvor nicht besprochen wurden. Ich werde einige Punkte erklären, die viele nicht verstehen. Wissen Sie, warum Sie die Umschalttaste oder die Strg-Taste drücken müssen?
Algorithmen zur Optimierung mit Populationen: Evolutionsstrategien, (μ,λ)-ES und (μ+λ)-ES Algorithmen zur Optimierung mit Populationen: Evolutionsstrategien, (μ,λ)-ES und (μ+λ)-ES
Der Artikel behandelt eine Gruppe von Optimierungsalgorithmen, die als Evolutionsstrategien (ES) bekannt sind. Sie gehören zu den allerersten Populationsalgorithmen, die evolutionäre Prinzipien für die Suche nach optimalen Lösungen nutzen. Wir werden Änderungen an den herkömmlichen ES-Varianten vornehmen und die Testfunktion und die Prüfstandsmethodik für die Algorithmen überarbeiten.