English Русский 日本語
preview
Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil 3): Verhaltensmuster 1

Entwurfsmuster in der Softwareentwicklung und MQL5 (Teil 3): Verhaltensmuster 1

MetaTrader 5Handel | 27 Februar 2024, 09:46
124 5
Mohamed Abdelmaaboud
Mohamed Abdelmaaboud

Einführung

In diesem Artikel setzen wir unsere Serie über das Thema Entwurfmuster (Design Patterns) im Softwarebereich fort. In den beiden vorangegangenen Artikeln dieser Serie haben wir zwei Arten dieser Muster identifiziert, nämlich die schöpferischen und die strukturellen Muster, und in diesem Artikel werden wir die Verhaltensmuster (Design Pattern) identifizieren, die die dritte Art sind, nachdem wir die Verhaltensmuster identifiziert und verstanden haben, und wie sie bei der Erstellung, dem Aufbau oder der Entwicklung unserer Software nützlich sein können.  Danach werden wir lernen, wie wir sie in MQL5 verwenden können, um unsere Software für MetaTrader 5 zu erstellen und eine zuverlässige, wartbare, wiederverwendbare, gut getestete und erweiterbare Software zu schaffen.

In den folgenden Themen werden wir auf diese wichtige Art von Mustern eingehen:

Wenn dies der erste Artikel in dieser Serie ist, hoffe ich, dass Sie auch andere Artikel über Erzeugungs- und Strukturmuster lesen, um sich einen Überblick über eines der wichtigsten Themen in der Softwareentwicklung zu verschaffen, nämlich Entwurfmuster, da diese bei der Erstellung Ihrer Software sehr nützlich sein können.

Haftungsausschluss: Alle Informationen werden in der vorliegenden Form nur zu Informationszwecken bereitgestellt und sind nicht für Handelszwecke oder als Ratschläge gedacht. Die Informationen garantieren keinen Erfolg. Wenn Sie sich dafür entscheiden, diese Materialien auf einem Ihrer Handelskonten zu verwenden, tun Sie dies auf eigenes Risiko und Sie sind allein verantwortlich.

Verhaltensmuster

Im Rahmen der Diskussion über Entwurfsmuster und nach der Diskussion über kreative und strukturelle Muster werden wir über die letzte Art dieser Entwurfsmuster sprechen, die Verhaltensmuster. Wir haben gelernt, dass Erzeugungsmuster die Muster sind, die dabei helfen, eine unabhängige Software oder ein System zu erstellen, indem sie Objekte erstellen, zusammensetzen und darstellen. Außerdem lernt man, dass Strukturmuster dazu dienen, größere Strukturen aufzubauen, indem man erstellte Objekte und Klassen verwendet.

In diesem Artikel werden wir die Verhaltensmuster vorstellen, die sich mit der Zuweisung und Einstellung von Verantwortlichkeiten zwischen Objekten befassen. Sie zeigen auch auf, wie Objekte miteinander kommunizieren oder interagieren können, und es gibt viele Muster diesen Typs:

  • Kette der Verantwortung
  • Anweisung
  • Interpreter
  • Iterator
  • Mediator
  • Memento
  • Beobachter
  • Status
  • Strategie
  • Methodenvorlage
  • Besucher

Da es viele Muster gibt, die nicht in einem Artikel behandelt werden können, werden wir uns in diesem Artikel nur auf die ersten fünf Muster konzentrieren, ebenso wie auf die folgenden:

  • Kette der Verantwortung (Chain of responsibility): Sie hilft bei der Entkopplung von Sender und Empfänger, indem sie mehreren Objekten die Möglichkeit gibt, die Anfrage des Absenders zu bearbeiten. Es ist auch hilfreich, Empfangsobjekte zu verketten und die Anfrage entlang dieser Kette weiterzuleiten, um die Bearbeitung eines Objekts abzuschließen.
  • Anweisung (Command): Sie gibt die Erlaubnis, Parameter für Clients mit verschiedenen Anfragen zu setzen, Anfragen in eine Warteschlange zu stellen oder zu protokollieren und rückgängig zu machende Operationen zu unterstützen, nachdem die Anfrage als Objekt gekapselt wurde.
  • Interpreter: Er definiert eine Repräsentation der Grammatik einer gegebenen Sprache zusammen mit einem Interpreter, der diese definierte Repräsentation verwenden kann, um das zu interpretieren, was als Sätze in der Sprache benötigt wird.
  • Iterator: Wenn wir einen sequenziellen Zugriff auf die Elemente eines Aggregatobjekts benötigen, ohne seine zugrunde liegende Darstellung offenzulegen, hilft uns dieses Muster, eine Methode dafür bereitzustellen.
  • Mediator: Er definiert, wie eine Gruppe von Objekten durch ein gekapseltes Objekt interagiert, und fördert die Entkopplung, indem er uns erlaubt, die Interaktion von Objekten unabhängig voneinander zu variieren.

Wenn Sie die beiden vorangegangenen Artikel über Entwurfmuster gelesen haben, werden Sie mit dem Ansatz vertraut sein, den wir für jedes Pattern verwenden werden:

  • Was bewirkt das Muster?
  • Was löst das Entwurfsmuster?
  • Wie können wir sie in MQL5 verwenden?


Kette der Verantwortung

In diesem Teil werden wir verstehen, was die Kette der Verantwortung (Chain of responsibility) ist, indem wir lernen, was sie tun und lösen kann, und wie wir sie in MQL5 verwenden können. Wenn wir eine Anfrage vom Client bearbeiten müssen, können wir dieses Muster verwenden, wenn wir viele Objekte haben, die die Anfragen des Clients basierend auf der Verantwortung jedes Einzelnen bearbeiten können.

Trotz der Vorteile, die die Verwendung dieses Musters mit sich bringt, gibt es auch einige Fallstricke, wie zum Beispiel die folgenden:

  • Effizienzproblem im Falle langer Ketten.
  • Es gibt keine Garantie für die Bearbeitung von Aufträgen, da der Auftrag keinen bestimmten Empfänger hat, sodass dieser Auftrag ohne Bearbeitung an Objekte in der Kette weitergegeben werden kann. Darüber hinaus müssen wir wissen, dass die Anfrage nicht bearbeitet werden kann, wenn wir keine ordnungsgemäß konfigurierte Kette haben.
Was bewirkt das Muster?

Dieses Muster kann nützlich sein, um den Absender einer Anfrage und den Empfänger dieser Anfrage zu entkoppeln, indem es vielen Objekten die Möglichkeit gibt, die Anfrage zu bearbeiten. Dies geschieht durch die Verkettung der empfangenden Objekte und die Weitergabe der Anfrage an alle, um zu prüfen, welches Objekt die Anfrage bearbeiten kann.

Das folgende Schaubild zeigt die Struktur des Musters:

CHAIN OF RESPONSIBILITY

Wie in der obigen Grafik zu sehen ist, haben wir folgende Teilnehmer:

  • Client: Der Client initiiert die Anfrage, die von dem Objekt in der Kette bearbeitet werden soll.
  • Handler: Er definiert die Schnittstelle zur Bearbeitung der Anfrage. Er kann auch die Nachfolgeverbindung herstellen.
  • ConcreteHandler: Es ist das Objekt, das die Anfrage auf der Grundlage seiner Zuständigkeit bearbeitet. Es hat Zugriff auf einen Nachfolger, den es weitergeben kann, wenn es die Anfrage nicht bearbeiten kann.

Was löst das Entwurfsmuster?

Dieses Muster kann verwendet werden, wenn die folgenden Punkte zutreffen:

  • Wir haben viele Objekte, die Anfragen bearbeiten können.
  • Wir müssen den Sender und den Empfänger entkoppeln.
  • Wir müssen eine Anfrage an eines von vielen Objekten stellen, ohne den Empfänger zu nennen.
  • Wir haben einen dynamisch festgelegten Satz von Objekten, die die Anfrage bearbeiten können.

Mit diesem Muster kann also Folgendes gelöst werden:

  • Kopplungsreduzierung, weil sie dazu beiträgt, Sender und Empfänger zu entkoppeln, was bedeutet, dass sie hilft, unabhängige Änderungen vorzunehmen.
  • Es bietet Flexibilität bei der Zuweisung und Verteilung von Zuständigkeiten an Objekte.

Wie können wir sie in MQL5 verwenden?

In diesem Teil werden wir lernen, wie man dieses Muster in MQL5 verwenden, um effektive MetaTrader5 Software zu erstellen, so, die folgenden Schritte sind, um die Kette der Verantwortung in der MQL5 Code:

Deklaration des Bereichs Chain_Of_Responsibility zur Aufnahme von Funktionen und Variablen des Musters mit Hilfe des Schlüsselworts „namespace“:

namespace Chain_Of_Responsibility

Deklarieren der Teilnehmer der Handler-Klasse, der die Anfragen vom Client bearbeitet und den Nachfolge-Link implementieren kann:

class Handler
  {
public:
   Handler*          successor;
   virtual void      HandleRequest(int)=0;
                    ~Handler(void);
  };
Handler::~Handler(void)
  {
   delete successor;
  }

Deklarieren der Teilnehmer der Klasse ConcreteHandler1, der die Anfragen bearbeitet, für die er verantwortlich ist, oder die Anfrage an seinen Nachfolger übergibt, wenn er dazu fähig ist:

class ConcreteHandler1:public Handler
  {
public:
   void              HandleRequest(int);
  };
void ConcreteHandler1::HandleRequest(int request)
  {
   if(request==1)
      Print("The request: ",request,". The request handled by: ",&this);
   else
      if(CheckPointer(successor))
        {
         Print("The request: ",request,". The request cannot be handled by ",&this,", but it is forwarding to the successor...");
         successor.HandleRequest(request);
        }
  }

Deklarieren der Klasse ConcreteHandler2 ebenfalls als Teilnehmer:

class ConcreteHandler2:public Handler
  {
public:
   void              HandleRequest(int);
  };
void ConcreteHandler2::HandleRequest(int request)
  {
   if(request==2)
      Print("The request: ",request,". The request handled by: ",&this);
   else
      if(CheckPointer(successor))
        {
         Print("The request: ",request,". The request cannot be handled by ",&this,", forwarding to successor...");
         successor.HandleRequest(request);
        }
  }

Deklarieren der Client-Klasse, die die Anfrage an den konkreten Handler in der Kette initiiert:

class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output()
  {
   return __FUNCTION__;
  }

Die Funktion der Ausführung des Clients besteht darin, die Anfrage an die Kette zu senden, damit sie bearbeitet oder an den Nachfolger weitergegeben werden kann:

void   Client::Run()
  {
   Handler* h1=new ConcreteHandler1();
   Handler* h2=new ConcreteHandler2();
   h1.successor=h2;
   h1.HandleRequest(1);
   h1.HandleRequest(2);
   delete h1;
  }

Im Folgenden finden Sie den vollständigen Code zur Verwendung des Musters der Verantwortungskette in MQL5 in einem Block:

//+------------------------------------------------------------------+
//|                                      Chain_Of_Responsibility.mqh |
//+------------------------------------------------------------------+
namespace Chain_Of_Responsibility
{
class Handler
  {
public:
   Handler*          successor;
   virtual void      HandleRequest(int)=0;
                    ~Handler(void);
  };
Handler::~Handler(void)
  {
   delete successor;
  }
class ConcreteHandler1:public Handler
  {
public:
   void              HandleRequest(int);
  };
void ConcreteHandler1::HandleRequest(int request)
  {
   if(request==1)
      Print("The request: ",request,". The request handled by: ",&this);
   else
      if(CheckPointer(successor))
        {
         Print("The request: ",request,". The request cannot be handled by ",&this,", but it is forwarding to the successor...");
         successor.HandleRequest(request);
        }
  }
class ConcreteHandler2:public Handler
  {
public:
   void              HandleRequest(int);
  };
void ConcreteHandler2::HandleRequest(int request)
  {
   if(request==2)
      Print("The request: ",request,". The request handled by: ",&this);
   else
      if(CheckPointer(successor))
        {
         Print("The request: ",request,". The request cannot be handled by ",&this,", but it is forwarding to successor...");
         successor.HandleRequest(request);
        }
  }
class Client
  {
public:
   string            Output();
   void              Run();
  };
string Client::Output()
  {
   return __FUNCTION__;
  }
void   Client::Run()
  {
   Handler* h1=new ConcreteHandler1();
   Handler* h2=new ConcreteHandler2();
   h1.successor=h2;
   h1.HandleRequest(1);
   h1.HandleRequest(2);
   delete h1;
  }
}


Anweisung

In diesem Teil werden wir ein weiteres Verhaltensmuster identifizieren, nämlich die Anweisung (Command), auch bekannt als Aktion und Transaktion. Dieses Muster hilft, die Anfrage in einem Objekt zu kapseln, und das erlaubt uns, unsere Parameter für verschiedene Anfragen einzustellen, ohne den Sender oder Empfänger zu ändern, was bedeutet, dass jetzt eine Entkopplung zwischen dem Sender, dem Prozessor und dem Empfänger stattfindet. Dies ist sehr hilfreich, wenn wir große Funktionalitäten innerhalb von Klassen haben. Dieses Muster unterstützt auch Operationen, die etwas rückgängig machen.

Wie bei den meisten Dingen gibt es auch bei der Verwendung dieses Musters einige Fallstricke, und diese sind die folgenden:

  • Es wird in der Regel in Kombination mit anderen Mustern verwendet.
  • Daraus ergibt sich eine große Anzahl von Klassen und Objekten, um alle verschiedenen Befehle oder Anfragen zu bearbeiten.
  • Die Erstellung eines Objekts für jeden Befehl verstößt gegen das objektorientierte Design.
Was bewirkt das Muster?

Es wird einfach ein gekapselter Aufrufer erstellt, der den Befehl empfängt und ihn an den Empfänger sendet.

Im Folgenden wird die Grafik des Befehlsmusters dargestellt:

COMMAND

Wie wir in der vorherigen Grafik der Struktur des Befehlsmusters sehen können, haben wir die folgenden Teilnehmer:

  • Command: Eine Anweisung (Command) kann die Schnittstelle zur Ausführung des Vorgangs deklariert werden.
  • ConcreteCommand: Dieser ConcreteCommand stellt eine Verbindung zwischen dem Empfänger und der Aktion bzw. dem Befehl her und führt darüber hinaus durch den Aufruf der entsprechenden Operation auf dem Empfänger die Ausführung durch.
  • Client: Hier können zwei Dinge angewendet werden: das Erstellen des ConcreteCommand und das Festlegen seines Empfängers.
  • Invoker: Er erhält den Befehl zur Ausführung der Anfrage.
  • Receiver: Der Receiver (Empfänger) identifiziert die Methode der mit der Ausführung des Ersuchens verbundenen Vorgänge und kann eine beliebige Klasse sein.

Was löst das Entwurfsmuster?

  • Wir können dieses Muster verwenden, wenn wir feststellen, dass die folgenden Fälle zutreffen:
  • Wir müssen den Objekten Parameter für die auszuführende Aktion zuweisen.
  • Wir müssen wie eine Spezifikation vorgehen, eine Warteschlange einrichten und zu verschiedenen Zeiten ausführen.
  • Wir brauchen etwas, das zur Unterstützung der Rückgängigmachung verwendet werden kann.
  • Im Falle eines Systemabsturzes benötigen wir Unterstützung für die Protokollierung von Änderungen, die in diesem Fall wieder angewandt werden sollen.
  • Wir brauchen hochrangige Operationen, die auf primitiven Operationen aufbauen, als Struktur des Systems.

Wie können wir sie in MQL5 verwenden?

In diesem Teil sehen wir uns eine Methode an, mit der dieses Befehlsmuster mit folgenden Schritten in MQL5 verwendet werden kann:

Deklaration unseres Befehlsraums zur Angabe unserer Funktionen, Variablen, Klassen ... usw. mit Hilfe des Schlüsselworts „namespace“:

namespace Command

Deklaration der Receiver-Klasse als Teilnehmer, der die Methode zur Durchführung der Vorgänge der Anfrage angibt:

class Receiver
  {
public:
                     Receiver(void);
                     Receiver(Receiver&);
   void              Action(void);
  };
Receiver::Receiver(void)
  {
  }
Receiver::Receiver(Receiver &src)
  {
  }
void Receiver::Action(void)
  {
  }

Deklaration der Klasse Command als Teilnehmer zur Deklaration der Operationsschnittstelle:

class Command
  {
protected:
   Receiver*         m_receiver;
public:
                     Command(Receiver*);
                    ~Command(void);
   virtual void      Execute(void)=0;
  };
Command::Command(Receiver* receiver)
  {
   m_receiver=new Receiver(receiver);
  }
Command::~Command(void)
  {
   if(CheckPointer(m_receiver)==1)
     {
      delete m_receiver;
     }
  }

Deklaration der Klasse ConcreteCommand als Teilnehmer, um die Verbindung zwischen dem Empfänger und der Aktion oder dem Befehl herzustellen und execute() nach dem Aufruf der Empfängeroperation zu implementieren:

class ConcreteCommand:public Command
  {
protected:
   int               m_state;
public:
                     ConcreteCommand(Receiver*);
   void              Execute(void);
  };
ConcreteCommand::ConcreteCommand(Receiver* receiver):
   Command(receiver),
   m_state(0)
  {
  }
void ConcreteCommand::Execute(void)
  {
   m_receiver.Action();
   m_state=1;
  }

Die Deklaration der Klasse Invoker als Teilnehmer, der den Befehl zur Ausführung der Anfrage erhält:

class Invoker
  {
public:
                    ~Invoker(void);
   void              StoreCommand(Command*);
   void              Execute(void);
protected:
   Command*          m_command;
  };
Invoker::~Invoker(void)
  {
   if(CheckPointer(m_command)==1)
     {
      delete m_command;
     }
  }
void Invoker::StoreCommand(Command* command)
  {
   m_command=command;
  }
void Invoker::Execute(void)
  {
   m_command.Execute();
  }

Deklaration der Klasse Client als Teilnehmer, um den konkreten Befehl zu erstellen, seinen Empfänger zu setzen und ihn auszuführen:

class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }
void Client::Run(void)
  {
   Receiver receiver;
   Invoker invoker;
   invoker.StoreCommand(new ConcreteCommand(&receiver));
   invoker.Execute();
  }

Der folgende Code ist also der vollständige Code in einem einzigen Codeblock:

//+------------------------------------------------------------------+
//|                                                      Command.mqh |
//+------------------------------------------------------------------+
namespace Command
{
class Receiver
  {
public:
                     Receiver(void);
                     Receiver(Receiver&);
   void              Action(void);
  };
Receiver::Receiver(void)
  {
  }
Receiver::Receiver(Receiver &src)
  {
  }
void Receiver::Action(void)
  {
  }
class Command
  {
protected:
   Receiver*         m_receiver;
public:
                     Command(Receiver*);
                    ~Command(void);
   virtual void      Execute(void)=0;
  };
Command::Command(Receiver* receiver)
  {
   m_receiver=new Receiver(receiver);
  }
Command::~Command(void)
  {
   if(CheckPointer(m_receiver)==1)
     {
      delete m_receiver;
     }
  }
class ConcreteCommand:public Command
  {
protected:
   int               m_state;
public:
                     ConcreteCommand(Receiver*);
   void              Execute(void);
  };
ConcreteCommand::ConcreteCommand(Receiver* receiver):
   Command(receiver),
   m_state(0)
  {
  }
void ConcreteCommand::Execute(void)
  {
   m_receiver.Action();
   m_state=1;
  }
class Invoker
  {
public:
                    ~Invoker(void);
   void              StoreCommand(Command*);
   void              Execute(void);
protected:
   Command*          m_command;
  };
Invoker::~Invoker(void)
  {
   if(CheckPointer(m_command)==1)
     {
      delete m_command;
     }
  }
void Invoker::StoreCommand(Command* command)
  {
   m_command=command;
  }
void Invoker::Execute(void)
  {
   m_command.Execute();
  }
class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void)
  {
   return __FUNCTION__;
  }
void Client::Run(void)
  {
   Receiver receiver;
   Invoker invoker;
   invoker.StoreCommand(new ConcreteCommand(&receiver));
   invoker.Execute();
  }
}


Interpreter

Ein weiteres Verhaltensmuster, das uns helfen kann, die Interaktion zwischen Objekten durch eine vorgegebene Sprache festzulegen und die Darstellung für Regeln oder Grammatik mit einem Interpreter zu definieren, der diese Darstellung später verwenden kann, um den Inhalt dieser Sprache zu erklären und zu interpretieren.

Bei der Verwendung dieser Art von Mustern gibt es einige Fallstricke, wie die folgenden:

  • Die Komplexität der Regeln oder der Grammatik, und wenn der Grad der Komplexität hoch ist, bedeutet dies Schwierigkeiten bei der Beibehaltung.
  • Sie kann in bestimmten Situationen eingesetzt werden.
Was bewirkt das Muster?

Dieses Muster hilft uns zu beschreiben, wie man die Grammatik einer Sprache definiert, den Inhalt dieser Sprache darstellt und eine Interpretation dieses Inhalts erhält.

Wenn wir das Diagramm des Musters des Interpreten sehen wollen, sieht es wie folgt aus:

Interpreter

Wie in der vorangegangenen Grafik zu sehen ist, sind die folgenden Teilnehmer an diesem Muster beteiligt:

  • AbstractExpression: Damit kann kann die Operationen der abstrakten Methode Interprete(context) deklariert werden.
  • TerminalExpression: Das kann die Operation des Interpretierens implementieren, die mit Symbolen des Terminals in der Grammatik verbunden ist, wobei eine Instanz als Voraussetzung für jedes Terminalsymbol im Inhalt erstellt wird.
  • NonterminalExression: Für jede Regel in der Grammatik ist eine Klasse erforderlich, die Instanzvariablen des AbstractExpression für jede Regel verwaltet. Sie implementiert die Operation des Interpret für nichtterminale Symbole in der Grammatik.
  • Kontext: Er enthält globale Informationen für den Interpreter.
  • Client: Damit wird der abstrakte Syntaxbaum aufgebaut, um den Inhalt in der Sprache darzustellen, die wir brauchen, um durch die Grammatik definiert zu werden, und es ruft die Operation des Interpretierens auf.

Was löst das Entwurfsmuster?

Wie wir festgestellt haben, kann dieses Muster verwendet werden, wenn wir eine Sprache haben, die wir interpretieren müssen, und wir können Inhalte in dieser Sprache definieren oder darstellen.

Im Folgenden sind die besten Fälle aufgeführt, für die wir das Muster verwenden können:

  • Wir haben eine einfache Grammatik der Sprache, denn wenn die Grammatik komplex ist, wird die Hierarchie der Klasse sehr groß, und das kann zu unkontrollierten Zuständen führen.
  • Wenn der Effizienzfaktor im Interpreter nicht so entscheidend ist.

Durch die Verwendung dieses Musters können wir also die folgenden Vorteile erzielen:

  • Die Verwendung dieses Musters macht es einfach und reibungslos, die Grammatik durch Vererbung zu aktualisieren oder zu erweitern.
  • Es macht auch die Umsetzung der Grammatik leicht.
  • Das macht es einfacher, neue Möglichkeiten zur Interpretation von Ausdrücken hinzuzufügen.

Wie können wir sie in MQL5 verwenden?

In diesem Teil des Artikels werden wir eine einfache Methode vorstellen, um diese Art von Muster zu kodieren oder zu verwenden. Im Folgenden werden die Schritte zur Verwendung dieses Interpreters in MQL5 beschrieben:

Deklarieren des Bereiches des Interpreters, den wir zum Definieren und Deklarieren unserer Funktionen, Variablen und Klassen verwenden werden, so wie wir es mit dem Schlüsselwort „namespace“ kennen:

namespace Interpreter

Deklarieren der Kontextklasse als Teilnehmer:

class Context
  {
public:
   string            m_source;
   char              m_vocabulary;
   int               m_position;
   bool              m_result;
   //---
                     Context(char,string);
   void              Result(void);
  };
Context::Context(char vocabulary,string source):
   m_source(source),
   m_vocabulary(vocabulary),
   m_position(0),
   m_result(false)
  {
  }
void Context::Result(void)
  {
  }

Deklarieren der abstrakten Klasse als Teilnehmer:

class AbstractExpression
  {
public:
   virtual void      Interpret(Context&)=0;
  };

Deklarieren der Klasse TerminalExpression als Teilnehmer, um die Methode „Interpret“ zu implementieren:

class TerminalExpression:public AbstractExpression
  {
public:
   void              Interpret(Context&);
  };
void TerminalExpression::Interpret(Context& context)
  {
   context.m_result=
      StringSubstr(context.m_source,context.m_position,1)==
      CharToString(context.m_vocabulary);
  }

Deklarieren der Klasse NonterminalExpression als Teilnehmer:

class NonterminalExpression:public AbstractExpression
  {
protected:
   AbstractExpression* m_nonterminal_expression;
   AbstractExpression* m_terminal_expression;
public:
   void              Interpret(Context&);
                    ~NonterminalExpression(void);
  };
NonterminalExpression::~NonterminalExpression(void)
  {
   delete m_nonterminal_expression;
   delete m_terminal_expression;
  }
void NonterminalExpression::Interpret(Context& context)
  {
   if(context.m_position<StringLen(context.m_source))
     {
      m_terminal_expression=new TerminalExpression;
      m_terminal_expression.Interpret(context);
      context.m_position++;
      if(context.m_result)
        {
         m_nonterminal_expression=new NonterminalExpression;
         m_nonterminal_expression.Interpret(context);
        }
     }
  }

Deklarieren des Client als Teilnehmer:

class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void) {return __FUNCTION__;}
void Client::Run(void)
  {
   Context context_1('a',"aaa");
   Context context_2('a',"aba");
   AbstractExpression* expression;
   expression=new NonterminalExpression;
   expression.Interpret(context_1);
   context_1.Result();
   delete expression;
   expression=new NonterminalExpression;
   expression.Interpret(context_2);
   context_2.Result();
   delete expression;
  }

Nachfolgend der vollständige Code für die Verwendung des Interpreter-Musters in einem einzigen Codeblock:

//+------------------------------------------------------------------+
//|                                                  Interpreter.mqh |
//+------------------------------------------------------------------+
namespace Interpreter
{
class Context
  {
public:
   string            m_source;
   char              m_vocabulary;
   int               m_position;
   bool              m_result;
                     Context(char,string);
   void              Result(void);
  };
Context::Context(char vocabulary,string source):
   m_source(source),
   m_vocabulary(vocabulary),
   m_position(0),
   m_result(false)
  {
  }
void Context::Result(void)
  {
  }
class AbstractExpression
  {
public:
   virtual void      Interpret(Context&)=0;
  };
class TerminalExpression:public AbstractExpression
  {
public:
   void              Interpret(Context&);
  };
void TerminalExpression::Interpret(Context& context)
  {
   context.m_result=
      StringSubstr(context.m_source,context.m_position,1)==
      CharToString(context.m_vocabulary);
  }
class NonterminalExpression:public AbstractExpression
  {
protected:
   AbstractExpression* m_nonterminal_expression;
   AbstractExpression* m_terminal_expression;
public:
   void              Interpret(Context&);
                    ~NonterminalExpression(void);
  };
NonterminalExpression::~NonterminalExpression(void)
  {
   delete m_nonterminal_expression;
   delete m_terminal_expression;
  }
void NonterminalExpression::Interpret(Context& context)
  {
   if(context.m_position<StringLen(context.m_source))
     {
      m_terminal_expression=new TerminalExpression;
      m_terminal_expression.Interpret(context);
      context.m_position++;
      if(context.m_result)
        {
         m_nonterminal_expression=new NonterminalExpression;
         m_nonterminal_expression.Interpret(context);
        }
     }
  }
class Client
  {
public:
   string            Output(void);
   void              Run(void);
  };
string Client::Output(void) {return __FUNCTION__;}
void Client::Run(void)
  {
   Context context_1('a',"aaa");
   Context context_2('a',"aba");
   AbstractExpression* expression;
   expression=new NonterminalExpression;
   expression.Interpret(context_1);
   context_1.Result();
   delete expression;
   expression=new NonterminalExpression;
   expression.Interpret(context_2);
   context_2.Result();
   delete expression;
  }
}


Iterator

Wir werden das Iterator-Entwurfsmuster identifizieren, das eines der Verhaltensmuster ist, um die Methode der Interaktion oder Kommunikation zwischen Objekten festzulegen. Dieses Muster hilft, dass ein aggregiertes Objekt wie eine Liste vorhanden ist, oder gibt uns eine Methode, um auf Elemente in einer sequenzierten Weise zuzugreifen, ohne die zugrunde liegenden Details der Darstellung oder der internen Struktur offen zu legen. Er ist auch als Cursor bekannt.

Trotz der Vorteile, die sich aus der Verwendung dieses Iterator-Musters ergeben, gibt es auch Fallstricke, wie unten beschrieben:

  • Wenn wir eine Sammlung haben, gibt es keinen Zugang zum Index dieser Sammlung.
  • Unidirektionale Zustand in einigen Fällen, wenn wir brauchen, um auf die vorherige zum Beispiel gerichtet werden, aber es gibt Lösungen für das in einigen Sprachen.
  • In manchen Fällen ist es schneller als die Verwendung des Musters, wenn wir einen Index erstellen und eine Schleife durch ihn ziehen.
Was bewirkt das Muster?

Dieses Muster kann Variationen im Falle komplexer Aggregate unterstützen, die auf unterschiedliche Weise durchlaufen werden können, da es einfach ist, den Durchlaufalgorithmus zu aktualisieren, indem die Instanz des Iterators ersetzt wird und zusätzlich Unterklassen für den Iterator definiert werden, um die aktualisierten Durchläufe zu unterstützen. Es macht die Schnittstelle des Aggregats einfach. Die Verfolgung des Traversal-Status des Iterators ermöglicht es, viele Traversals auf einmal zu verarbeiten.

Der Graph dieses Musters entspricht dem folgenden Diagramm:

ITERATOR

Auf der Grundlage des vorherigen Diagramms der Iterator-Struktur können wir die folgenden Teilnehmer finden:

  • Iterator: Er definiert die Schnittstelle, die für den Zugriff auf Elemente und deren Durchlaufen verwendet werden kann.
  • ConcreteIterator: Das ermöglicht die Implementierung der Schnittstelle des Iterators und verfolgt die aggregierte Traversierung.
  • Aggregat: Das identifiziert die Schnittstelle, die zur Erstellung des Objekt-Iterators verwendet werden kann.
  • ConcreteAggregate: Es kann hilfreich sein, die Iterator-Schnittstelle zu implementieren, um die Instanz des passenden ConcreteIterators als Rückgabe zu erhalten.

Was löst das Entwurfsmuster?

Dieses Iteratormuster kann verwendet werden, wenn folgendes zutrifft:

  • Wenn wir auf den Inhalt des Aggregatobjekts zugreifen müssen, aber nicht die interne Darstellung des Objekts offenlegen müssen.
  • Wenn wir viele Traversalen von Aggregatobjekten haben, müssen wir das unterstützen.
  • Wenn wir verschiedene Aggregatstrukturen haben, müssen wir eine einheitliche Schnittstelle für deren Durchlaufen anbieten.

Wie können wir sie in MQL5 verwenden?

In diesem Teil werden wir lernen, wie wir dieses Muster in MQL5 anhand der folgenden Schritte verwenden können:

Definition von ERRITERAOR-UT-OF-BOUNDS mit Hilfe des Präprozessors #define

#define ERR_ITERATOR_OUT_OF_BOUNDS 1

Mit Hilfe von template und T deklarieren wir das Interface Iterator:

template<typename T>
interface Iterator
  {
   void     First(void);
   void     Next(void);
   bool     IsDone(void);
   T        CurrentItem(void);
  };

Außerdem deklarieren wir mit template und T das Interface Aggregate:

template<typename T>
interface Aggregate
  {
   Iterator<T>*   CreateIterator(void);
   int            Count(void);
   T              operator[](int at);
   void           operator+=(T item);
  };

Implementierung der Iterator-Schnittstelle und Verfolgung des Aggregatdurchlaufs als aktuelle Position nach Deklaration der Klasse ConcreteIterator:

template<typename T>
class ConcreteIterator:public Iterator<T>
  {
public:
   void              First(void);
   void              Next(void);
   bool              IsDone(void);
   T                 CurrentItem(void);
                     ConcreteIterator(Aggregate<T>&);
protected:
   Aggregate<T>*     m_aggregate;
   int               m_current;
  };
template<typename T> 
   ConcreteIterator::ConcreteIterator(Aggregate<T>& aggregate):
   m_aggregate(&aggregate),
   m_current(0)
  {
  }
template<typename T>
void ConcreteIterator::First(void)
  {
   m_current=0;
  }
template<typename T>
void ConcreteIterator::Next(void)
  {
   m_current++;
   if(!IsDone())
     {
     }
  }
template<typename T>
bool ConcreteIterator::IsDone(void)
  {
   return m_current>=m_aggregate.Count();
  }
template<typename T>
string ConcreteIterator::CurrentItem(void)
  {
   if(IsDone())
     {
      SetUserError(ERR_ITERATOR_OUT_OF_BOUNDS);
      return NULL;
     }
   return m_aggregate[m_current];
  }

Implementierung der Iteratorerstellungsschnittstelle, um die Instanz des geeigneten konkreten Iterators als Rückgabewert zu erhalten:

class ConcreteAggregate:public Aggregate<string>
  {
public:
   Iterator<string>* CreateIterator(void);
   int               Count(void);
   void              operator+=(string item);
   string            operator[](int at);
protected:
   string            m_items[];
  };
Iterator<string>* ConcreteAggregate::CreateIterator(void)
  {
   return new ConcreteIterator<string>(this);
  }
void ConcreteAggregate::operator+=(string item)
  {
   int size=ArraySize(m_items);
   ArrayResize(m_items,size+1);
   m_items[size]=item;
  }
string ConcreteAggregate::operator[](int at)
  {
   return m_items[at];
  }
int ConcreteAggregate::Count()
  {
   return ArraySize(m_items);
  }

Im Folgenden finden Sie den vollständigen Code in einem Codeblock zur Verwendung des Iterator-Musters in MQL5:

//+------------------------------------------------------------------+
//|                                                201021_104101.mqh |
//+------------------------------------------------------------------+
#define ERR_ITERATOR_OUT_OF_BOUNDS 1
template<typename T>
interface Iterator
  {
   void     First(void);
   void     Next(void);
   bool     IsDone(void);
   T        CurrentItem(void);
  };
template<typename T>
interface Aggregate
  {
   Iterator<T>*   CreateIterator(void);
   int            Count(void);
   T              operator[](int at);
   void           operator+=(T item);
  };

template<typename T>
class ConcreteIterator:public Iterator<T>
  {
public:
   void              First(void);
   void              Next(void);
   bool              IsDone(void);
   T                 CurrentItem(void);
                     ConcreteIterator(Aggregate<T>&);
protected:
   Aggregate<T>*     m_aggregate;
   int               m_current;
  };
template<typename T> 
   ConcreteIterator::ConcreteIterator(Aggregate<T>& aggregate):
   m_aggregate(&aggregate),
   m_current(0)
  {
  }
template<typename T>
void ConcreteIterator::First(void)
  {
   m_current=0;
  }
template<typename T>
void ConcreteIterator::Next(void)
  {
   m_current++;
   if(!IsDone())
     {
     }
  }
template<typename T>
bool ConcreteIterator::IsDone(void)
  {
   return m_current>=m_aggregate.Count();
  }
template<typename T>
string ConcreteIterator::CurrentItem(void)
  {
   if(IsDone())
     {
      SetUserError(ERR_ITERATOR_OUT_OF_BOUNDS);
      return NULL;
     }
   return m_aggregate[m_current];
  }
class ConcreteAggregate:public Aggregate<string>
  {
public:
   Iterator<string>* CreateIterator(void);
   int               Count(void);
   void              operator+=(string item);
   string            operator[](int at);
protected:
   string            m_items[];
  };
Iterator<string>* ConcreteAggregate::CreateIterator(void)
  {
   return new ConcreteIterator<string>(this);
  }
void ConcreteAggregate::operator+=(string item)
  {
   int size=ArraySize(m_items);
   ArrayResize(m_items,size+1);
   m_items[size]=item;
  }
string ConcreteAggregate::operator[](int at)
  {
   return m_items[at];
  }
int ConcreteAggregate::Count()
  {
   return ArraySize(m_items);
  }


Mediator

Ein weiteres Verhaltensmuster, mit dem festgelegt werden kann, wie Objekte miteinander interagieren können. Dieses Muster ist das Mediator-Muster, das verwendet werden kann, wenn wir eine Gruppe von Objekten haben und ein gekapseltes Objekt definieren müssen, um zu beschreiben, wie diese Gruppe von Objekten interagieren kann. Es erlaubt oder wendet auch Entkopplung an, was nützlich sein kann und uns die Interaktion von Objekten unabhängig variieren lässt.

Wie alles, was Vorteile und Fallstricke haben kann, gibt es auch für das Mediator-Entwurfsmuster die folgenden Fallstricke:

  • Für alles gibt es einen geschaffenen Mediator.
  • Es wird mit anderen Mustern verwendet.
Was bewirkt das Muster?

Gemäß dem, was wir als Identifikation für das Mediator-Muster erwähnt haben, können wir sagen, dass es hilft, die Interaktionsmethode zwischen Objekten festzulegen, ohne jedes Objekt explizit zu erwähnen. Es hilft also, die Entkopplung zwischen den Objekten anzuwenden. Es kann auch als Router verwendet werden und dient dem Kommunikationsmanagement.

Das folgende Diagramm zeigt die Struktur des Mediator-Musters:

Mediator

Mediator(2)

Wie wir in der vorherigen Grafik der Struktur des Musters sehen können, haben wir die folgenden Teilnehmer für das Mediator-Muster:

  • Mediator: Er bezeichnet die Schnittstelle zur Kommunikation mit Objekten von Kollegen.
  • ConcreteMediator: Durch die Koordinierung von Objekten von Kollegen implementiert er das Verhalten der Kooperation. Seine Kollegen sind bekannt und können vom ConcreteMediator gewartet werden.
  • Die Klassen der Kollegen: Der Mediator des Objekts ist jeder Klasse der Kollegen bekannt. Darüber hinaus kann jeder Kollege jederzeit mit seinem Vermittler kommunizieren, wenn diese Kommunikation erforderlich ist oder mit einem anderen Kollegen kommunizieren will.

Was löst das Entwurfsmuster?

Nach allem, was wir bisher verstanden haben, kann dieses Muster verwendet werden, um folgende Aufgaben zu lösen:

  • Wir haben eine Reihe von Objekten, die miteinander kommunizieren können, nachdem wir die Art und Weise der Kommunikation definiert haben, aber die Art und Weise dieser Kommunikation ist komplex.
  • Es ist schwierig, ein Objekt wiederzuverwenden, weil es mit verschiedenen anderen Objekten kommuniziert.
  • Wir müssen das Verhalten, das zwischen den Klassen verteilt ist, anpassen, ohne viele Unterklassen zu erstellen.

Das können wir also sagen:

  • Es hilft, die Unterklassenbildung einzuschränken.
  • Es hilft, die Kollegen zu entkoppeln.
  • Es macht Objektprotokolle einfach.
  • Die Zusammenarbeit zwischen Objekten wird abstrahiert.
  • Die Kontrolle ist zentralisiert.

Wie können wir sie in MQL5 verwenden?

Wenn wir wissen wollen, wie wir dieses Muster in MQL5 verwenden können, können wir das mit Hilfe der folgenden Schritte tun:

Verwendung des Schlüsselworts interface zur Erstellung der Colleague-Schnittstelle:

interface Colleague
  {
   void Send(string message);
  };
Verwendung des Schlüsselworts interface zur Erstellung der Mediator-Schnittstelle:
interface Mediator
  {
   void Send(string message,Colleague& colleague);
  };

Deklarieren der Klasse ConcreteColleague1:

class ConcreteColleague1:public Colleague
  {
protected:
   Mediator*         m_mediator;
public:
                     ConcreteColleague1(Mediator& mediator);
   void              Notify(string message);
   void              Send(string message);
  };
ConcreteColleague1::ConcreteColleague1(Mediator& meditor):
   m_mediator(&meditor)
  {
  }
void ConcreteColleague1::Notify(string message)
  {
  }
void ConcreteColleague1::Send(string message)
  {
   m_mediator.Send(message,this);
  }

Deklarieren der Klasse ConcreteColleague2:

class ConcreteColleague2:public Colleague
  {
protected:
   Mediator*         m_mediator;
public:
                     ConcreteColleague2(Mediator& mediator);
   void              Notify(string message);
   void              Send(string message);
  };
ConcreteColleague2::ConcreteColleague2(Mediator& mediator):
   m_mediator(&mediator)
  {
  }
void ConcreteColleague2::Notify(string message)
  {
  }
void ConcreteColleague2::Send(string message)
  {
   m_mediator.Send(message,this);
  }

Deklarieren der Klasse ConcreteMediator:

class ConcreteMediator:public Mediator
  {
public:
   ConcreteColleague1*  colleague_1;
   ConcreteColleague2*  colleague_2;
   void              Send(string message,Colleague& colleague);
  };
void ConcreteMediator::Send(string message,Colleague& colleague)
  {
   if(colleague_1==&colleague)
      colleague_2.Notify(message);
   else
      colleague_1.Notify(message);
  }

Wir können also den vollständigen Code zur Verwendung des Mediator-Entwurfsmusters in MQL5 in einem Codeblock wie dem folgenden finden:

//+------------------------------------------------------------------+
//|                                                     Mediator.mqh |
//+------------------------------------------------------------------+
interface Colleague
  {
   void Send(string message);
  };
interface Mediator
  {
   void Send(string message,Colleague& colleague);
  };
class ConcreteColleague1:public Colleague
  {
protected:
   Mediator*         m_mediator;
public:
                     ConcreteColleague1(Mediator& mediator);
   void              Notify(string message);
   void              Send(string message);
  };
ConcreteColleague1::ConcreteColleague1(Mediator& meditor):
   m_mediator(&meditor)
  {
  }
void ConcreteColleague1::Notify(string message)
  {
  }
void ConcreteColleague1::Send(string message)
  {
   m_mediator.Send(message,this);
  }
class ConcreteColleague2:public Colleague
  {
protected:
   Mediator*         m_mediator;
public:
                     ConcreteColleague2(Mediator& mediator);
   void              Notify(string message);
   void              Send(string message);
  };
ConcreteColleague2::ConcreteColleague2(Mediator& mediator):
   m_mediator(&mediator)
  {
  }
void ConcreteColleague2::Notify(string message)
  {
  }
void ConcreteColleague2::Send(string message)
  {
   m_mediator.Send(message,this);
  }
class ConcreteMediator:public Mediator
  {
public:
   ConcreteColleague1*  colleague_1;
   ConcreteColleague2*  colleague_2;
   void              Send(string message,Colleague& colleague);
  };
void ConcreteMediator::Send(string message,Colleague& colleague)
  {
   if(colleague_1==&colleague)
      colleague_2.Notify(message);
   else
      colleague_1.Notify(message);
  }


Schlussfolgerung

Es wird angenommen, dass Sie nun Informationen über die dritte Art von Entwurfsmustern erhalten haben, die eines der wichtigsten Themen in der Programmierung und Softwareentwicklung ist. Wir haben in diesem Artikel einige Verhaltensmuster vorgestellt und besprochen, was sie sind und wie sie nützlich sein können, um wiederverwendbare, erweiterte, wartbare und getestete Software durch sie zu erstellen. Probleme oder Fragen, die sie lösen, indem jedes Muster mit Vorteilen und Fallstricken von jedem Muster wurden erläutert, und wie jedes Muster in der MQL5 verwendet werden kann, um effektive Handelssysteme für den MetaTrader 5 zu schaffen.

Wir haben die folgenden Muster aus den verhaltensorientierten Entwurfsmustern erwähnt:

  • Kette der Verantwortlichkeiten.
  • Anweisung
  • Interpreter
  • Iterator
  • Mediator

Wenn dies der erste Artikel ist, den Sie bei mir über Entwurfmuster lesen, empfehle ich Ihnen, meine anderen Artikel über Entwurfmuster in der Softwareentwicklung und MQL5 (Teil I) zu lesen: Erzeugungsmuster und Entwurfmuster in der Softwareentwicklung und MQL5 (Teil 2): Strukturelle Muster, wenn Sie mehr über andere Arten von Entwurfsmustern erfahren möchten, und ich hoffe, dass Sie sie nützlich finden.

Ich empfehle auch, mehr über das Thema Entwurfmuster zu lesen, da es Ihnen helfen wird, effektive Software zu erstellen. Im Folgenden finden Sie einige nützliche Ressourcen zu diesem Thema:

  • Design Patterns - Elements of Reusable Object-Oriented Software by Eric Gamma, Richard Helm, Ralph Johnson, and John Vlissides
  • Design Patterns for Dummies by Steve Holzner
  • Head First Design Patterns by Eric Freeman, Elisabeth Robson, Bert Bates, and Kathy Sierra

Wenn Sie weitere Artikel über die Erstellung von Handelssystemen für den MetaTrader 5 unter Verwendung der beliebtesten technischen Indikatoren lesen möchten, können Sie meine anderen Artikel dazu auf meiner Publikationsseite lesen. Ich hoffe, dass Sie sie für Ihren Handel nützlich finden, um nützliche Einblicke zu erhalten und Ihre Ergebnisse zu verbessern oder Ihren Hintergrund als Entwickler zu entwickeln, um Projekte zu verbessern, an denen Sie arbeiten.

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

Beigefügte Dateien |
Command.mqh (4.04 KB)
Interpreter.mqh (4.83 KB)
Iterator.mqh (2.31 KB)
Mediator.mqh (1.72 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (5)
Maxim Kuznetsov
Maxim Kuznetsov | 4 Apr. 2024 in 17:22

nach

brauchen Sie nicht weiter zu lesen

Rashid Umarov
Rashid Umarov | 5 Apr. 2024 in 08:51
Maxim Kuznetsov #:

nach

brauchen Sie nicht weiter zu lesen

Das ist eine Übersetzung aus dem englischen Original.


Sie können es gedanklich durch "handler" ersetzen. Oder lesen Sie es einfach nicht und schreiben Sie Ihre eigenen Artikel.

Artikel zum Thema https://habr.com/ru/articles/113995/

Maxim Kuznetsov
Maxim Kuznetsov | 5 Apr. 2024 in 17:45
Rashid Umarov #:

Dies ist eine Übersetzung des englischen Originals.


Sie können es gedanklich durch "handler" ersetzen. Oder lesen Sie es einfach nicht, sondern schreiben Sie Ihre eigenen Artikel.

Artikel zum Thema https://habr.com/ru/articles/113995/

es geht nicht um die Übersetzung... es gibt fast keinen Text in dem Artikel, deshalb gibt es eine strenge Voreingenommenheit gegenüber dem Code.

Hat der Code aus dem Screenshot eine Chance, die Code-Überprüfung zu bestehen?

und über "write your own" - Sie wissen Bescheid, ich habe eine Serie über die Verwendung von gcc und msys2-Umgebung vorgeschlagen, aber es hat sich herausgestellt, dass man MSVC nicht ausnehmen kann

trampampam
trampampam | 5 Apr. 2024 in 21:21
Maxim Kuznetsov #:

Was ist Ihrer Meinung nach das "Richtige" zu tun?

Denis Kirichenko
Denis Kirichenko | 22 Apr. 2024 in 12:52
template<typename T>
void ConcreteIterator::Next(void)
  {
   m_current++;
   if(!IsDone())
     {
     }
  }


Wozu istdas überhaupt gut? Wenn man sich das Material über Iteratoren ansieht, gibt es diese Optionen:

1)

template<typename T>
void ConcreteIterator::Next(void)
  {
   m_current++;
  }

2)

template<typename T>
void ConcreteIterator::Next(void)
  {   
   if(!IsDone())
     {
       m_current++;
     }
  }
Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 4): Triangulärer gleitender Durchschnitt — Indikatorensignale Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 4): Triangulärer gleitender Durchschnitt — Indikatorensignale
Der Multi-Currency Expert Advisor in diesem Artikel ist ein Expert Advisor oder Handelsroboter, der mehr als nur ein Symbolpaar von dessen Symbolchart handeln kann (Aufträge öffnen, schließen und verwalten oder zum Beispiel Trailing Stop Loss und Trailing Profit). Dieses Mal werden wir nur 1 Indikator verwenden, nämlich den Triangulären gleitenden Durchschnitt in Multi-Timeframes oder Single-Timeframes.
Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 5):  Die Bollinger Bänder mit dem Keltner-Kanal — Indikatoren Signal Wie man einen einfachen Multi-Currency Expert Advisor mit MQL5 erstellt (Teil 5): Die Bollinger Bänder mit dem Keltner-Kanal — Indikatoren Signal
Der Multi-Currency Expert Advisor in diesem Artikel ist ein Expert Advisor oder Handelsroboter, der handeln kann (z.B. Aufträge eröffnen, schließen und verwalten, Trailing Stop Loss und Trailing Profit) für mehr als ein Symbolpaar aus nur einem Symbolchart. In diesem Artikel werden wir Signale von zwei Indikatoren verwenden, in diesem Fall Bollinger Bänder® und dem Keltner Kanal.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 08): Perceptrons MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 08): Perceptrons
Perceptrons, Netze mit einer einzigen ausgeblendeten Schicht, sind ein guter Einstieg für alle, die mit den Grundlagen des automatisierten Handels vertraut sind und sich mit neuronalen Netzen vertraut machen wollen. Wir sehen uns Schritt für Schritt an, wie dies in einer Signalklassen-Assembly realisiert werden könnte, die Teil der MQL5 Wizard-Klassen für Expert Advisors ist.
Modifizierter Grid-Hedge EA in MQL5 (Teil I): Erstellung eines einfachen Hedge EA Modifizierter Grid-Hedge EA in MQL5 (Teil I): Erstellung eines einfachen Hedge EA
Wir werden einen einfachen Hedge EA als Basis für unseren fortgeschritteneren Grid-Hedge EA erstellen, der eine Mischung aus klassischen Grid- und klassischen Hedge-Strategien sein wird. Am Ende dieses Artikels werden Sie wissen, wie Sie eine einfache Hedge-Strategie erstellen können, und Sie werden auch erfahren, was die Leute darüber sagen, ob diese Strategie wirklich zu 100 % profitabel ist.