English Русский 中文 Español 日本語 Português
preview
Techniken des MQL5-Assistenten, die Sie kennen sollten (Teil 02): Kohonen-Karten

Techniken des MQL5-Assistenten, die Sie kennen sollten (Teil 02): Kohonen-Karten

MetaTrader 5Handelssysteme | 18 Juli 2022, 11:18
248 0
Stephen Njuki
Stephen Njuki

 

1. Einführung

1.1 In der Fortsetzung dieser Serie über den MQL5-Assistenten werden wir uns diesmal mit Kohonen-Maps (Kohnen-Karten) beschäftigen. Diese per Wikipedia werden verwendet, um eine niedrigdimensionale (typischerweise zweidimensionale) Darstellung eines höherdimensionalen Datensatzes zu erzeugen, wobei die topologische Struktur der Daten erhalten bleibt. Sie wurden in den 1980er Jahren von Teuvo Kohonen entwickelt.

Einfach ausgedrückt sind Kohonen-Maps (auch selbstorganisierende Karten genannt) in der Lage, Komplexität zusammenzufassen, ohne die Unterscheidbarkeit des Zusammengefassten zu verlieren. Die Zusammenfassung dient als eine Form der Organisation, daher der Name Selbstorganisation. Mit den neu organisierten Daten oder Karten haben wir also zwei zusammenhängende Datensätze. Die ursprünglichen hochdimensionalen Daten, die uns als Eingabe dienen, und die zusammengefasste Form (niedrigerdimensionale Daten), die normalerweise, aber nicht immer, in zwei Dimensionen dargestellt wird, als Ausgabe. Die Eingaben sind das Bekannte, während die Ausgaben das Unbekannte oder in diesem Fall das, was "untersucht" wird.

Für einen Händler, wenn wir uns in diesem Artikel nur auf zeitbasierte Preisreihen konzentrieren, sind die bekannten Daten (die wir als Feed-Daten bezeichnen) zu jeder Zeit die Preise links von dieser Zeit mit den unbekannten Daten (die wir Funktor-Daten nennen), die auf der rechten Seite liegen. Die Art und Weise, wie wir die bekannten und unbekannten Daten klassifizieren, bestimmt die jeweilige Anzahl der Dimensionen sowohl für die Feed- als auch für die Funktordaten. Dies ist etwas, das Händler kritisch betrachten sollten, da es in hohem Maße von ihren Ansichten und ihrer Herangehensweise an die Märkte abhängt.  

1.2 Ein häufiges Missverständnis bei diesen Karten ist, dass die Funktordaten ein Bild oder zweidimensional sein sollten. Bilder wie das folgende werden oft als repräsentativ für die Kohonen-Karten angesehen.

typisches_Bild

Es ist zwar nicht falsch, aber ich möchte betonen, dass der Funktor eine einzige Dimension haben kann und vielleicht auch sollte (für Händler). Anstatt also unsere hochdimensionalen Daten auf eine 2D-Karte zu reduzieren, werden wir sie auf eine einzelne Linie abbilden. Kohonen-Karten sind per Definition dazu gedacht, die Dimensionalität zu reduzieren, daher möchte ich in diesem Artikel einen Schritt weiter gehen. Die Kohonen-Karte unterscheidet sich von normalen neuronalen Netzen sowohl durch die Anzahl der Schichten als auch durch den zugrunde liegenden Algorithmus. Es handelt sich um eine einschichtige (in der Regel lineare 2D-Gitter, wie bereits erwähnt) Menge von Neuronen anstelle mehrerer Schichten. Alle Neuronen dieser Schicht, die wir als Funktor bezeichnen, sind mit dem Feed verbunden, aber nicht mit sich selbst. Das bedeutet, dass die Neuronen nicht direkt von den Gewichten der anderen Neuronen beeinflusst werden, sondern nur in Bezug auf die Feed-Daten aktualisiert werden. Die Funktordatenschicht ist häufig eine "Landkarte", die sich bei jeder Trainingsiteration in Abhängigkeit von den eingespeisten Daten neu organisiert. So hat jedes Neuron nach dem Training eine gewichtsangepasste Dimension in der Funktorschicht, die es ermöglicht, den euklidischen Abstand zwischen zwei beliebigen Neuronen zu berechnen.

 

2. Erstellen der Klasse

2.1 Struktur der Klasse.

2.1.1 Die abstrakte Klasse Dimension ist die erste Klasse, die wir definieren werden. Dieser Code wäre aufgeräumter, wenn ich das meiste davon in einer separaten Datei gemacht hätte und einfach darauf verweisen würde, aber ich möchte das zusammen mit den Geld- und Trailing-Klassen im nächsten Artikel abdecken, sodass im Moment wie im vorherigen Artikel der gesamte Code in der Signaldatei sein wird. Die Dimensionen sind in diesem Netz immer wichtig, da sie den Output stark beeinflussen. Die Eingabedaten (die Inputs) sind in der Regel mehrdimensional. Die Daten des Funktors (die Ausgänge) haben eine Dimension im Gegensatz zu den typischen x und y. Aufgrund der Mehrdimensionalität sowohl der Feed- als auch der Funktordaten wäre ein idealer Datentyp ein Double-Array.

Um jedoch dem Trend der Erforschung der MQL5-Bibliothek zu folgen, werden wir stattdessen eine Array-Liste vom Typ Double verwenden. Die Feed-Daten sind Änderungen der Tiefstwerte abzüglich der Änderungen der Höchstwerte über einen Zeitraum von einem Balken, wie wir ihn im vorherigen Artikel verwendet hatten. In der Regel werden Inputs besser auf der Grundlage der Marktkenntnisse eines Händlers ausgewählt und sollten nicht von jedem auf einem Live- oder sogar Testkonto verwendet werden. Jeder Händler sollte diesen Code ändern, um seine eigenen Eingabedaten zu berücksichtigen. Die Daten des Funktors werden wie angegeben eindimensional sein. Da es sich jedoch auch um eine Liste handelt, kann sie angepasst werden, um weitere Dimensionen hinzuzufügen. Für unsere Zwecke werden wir uns jedoch auf die Veränderung zwischen dem Eröffnungs- und dem Schlusskurs des letzten Balkens konzentrieren. Auch hier können wir mit dem MQL5-Assistenten festlegen, was ein Balken ist, indem wir Ihren eigenen Zeitrahmen auswählen. Die Klasse Dimension wird von CArrayList aus der MQL5-Codebibliothek abgeleitet. Diese Klasse wird um zwei Funktionen erweitert, nämlich Get und Set. Wie der Name schon sagt, helfen sie beim Abrufen und Setzen von Werten in der Liste, sobald ein Index angegeben ist.

#include                        <Generic\ArrayList.mqh>
#include                        <Generic\HashMap.mqh>

#define                         SCALE 5

#define                         IN_WIDTH 2*SCALE
#define                         OUT_LENGTH 1

#define                         IN_RADIUS 100.0
#define                         OUT_BUFFER 10000

//
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Cdimension                :  public CArrayList<double>
  {
    public:
        
      Cdimension()              {};
      ~Cdimension()             {};
        
     virtual double             Get(const int Index)                                    
                                {       
                                  double _value=0.0; TryGetValue(Index,_value); return(_value); 
                                };
     virtual void               Set(const int Index,double Value)       
                                {  
                                  Insert(Index,Value);                                                                                                                   
                                };
  };


2.1.2 Die Klasse Feed wird von der soeben erstellten Klasse Dimension abgeleitet. Hier werden keine besonderen Funktionen hinzugefügt. Nur der Konstruktor gibt die Kapazität der Liste an (analog zur Array-Größe) und die Standardgröße unserer Feed-Data ist 10.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Cfeed             : public Cdimension
 {
   public:
                
     Cfeed()            { Clear(); Capacity(IN_WIDTH);  };
     ~Cfeed()           {                               };
 };


2.1.3 Functor Die Klasse wird ähnlich wie die Feed-Klasse sein, mit dem einzigen Unterschied in der Größe. Wie bereits erwähnt, betrachten wir 1 und nicht die üblichen 2 Dimensionen für unsere Funktordaten, sodass die Mengengröße 1 beträgt.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Cfunctor          : public Cdimension
 {
   public:
                
   Cfunctor()           { Clear(); Capacity(OUT_LENGTH); };
   ~Cfunctor()          {                                };
 };


2.1.4 Neuron Diese Klasse ist der Punkt, an dem unser Code interessant wird. Wir werden sie als Klasse deklarieren, die von einer Schnittstelle in der MQL5-Bibliothek erbt, die zwei nutzerdefinierte Datentypen annimmt. Ein Schlüssel und ein Wert. Die betreffende Vorlagenschnittstelle ist die HashMap.  Und die nutzerdefinierten Daten, die wir verwenden werden, sind die beiden oben angegebenen Klassen. Nämlich die Klasse Feed als Schlüssel und die Klasse Functor als Wert. Wir haben auch keine Funktionen, sondern nur Zeiger auf die Feed-Klasse, die Functor-Klasse und eine "Key-Value"-Klasse desselben. Der Zweck dieser Klasse ist, wie der Name schon sagt, die Definition des Neurons. Das Neuron ist unsere Dateneinheit, da es sowohl Eingangsdaten (Feeddaten) als auch Ausgangsdaten (Funktordaten) enthält. Die Futterdaten eines Neurons werden mit bereits trainierten Neuronen abgeglichen, um zu projizieren, was der Funktor sein könnte. Auch bei den abgebildeten Neuronen werden die Funktordaten angepasst, wenn ein neues Neuron trainiert wird.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Cneuron           : public CHashMap<Cfeed*,Cfunctor*>
 {
   public:
                
    double              weight;
                        
    Cfeed               *fd;
    Cfunctor            *fr;
                        
    CKeyValuePair
    <
    Cfeed*,
    Cfunctor*
    >                   *ff;
                        
    Cneuron()           {
                          weight=0.0;
                          fd = new Cfeed();
                          fr = new Cfunctor();
                          ff = new CKeyValuePair<Cfeed*,Cfunctor*>(fd,fr);
                          Add(ff);
                        };
                                                                        
   ~Cneuron()           {
                          ZeroMemory(weight);
                          delete fd;
                          delete fr;
                          delete ff;
                        };
 };


2.1.5 Layer Diese abstrakte Klasse ist das, was als Nächstes folgt. Sie erbt von einer Listenvorlage der Neuronenklasse und hat ein Objekt, einen Neuronenzeiger. Da es sich um eine abstrakte Klasse handelt, soll dieser Neuronenzeiger von Klassen verwendet werden, die von dieser Klasse abgeleitet werden. Diese Klassen sind 2, nämlich die Eingabeschicht und die Ausgabeschicht. Streng genommen sollten Kohonen-Karten nicht als neuronale Netze eingestuft werden, da sie keine Feed-Forward-Verbindungen mit Gewichten und Backpropagation haben. Einige Befürworter sind jedoch der Meinung, dass es sich um einen anderen Typ handelt. 

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Clayer            : public CArrayList<Cneuron*>
 {
   public:
                
    Cneuron             *n;
                
    Clayer()            { n = new Cneuron();     };
    ~Clayer()           { delete n;              };
 };


2.1.6 Input Layer Die Klasse wird von der abstrakten Klasse CLayer abgeleitet. Hier werden die aktuellen Werte der Dateneinspeisung gespeichert, wenn das Netz verwendet wird. Anstatt einer typischen Schicht (layer) mit mehreren Neuronen wird es ein einziges Neuron geben, das die aktuellsten Feed- und Funktordaten hat, daher wird seine Größe 1 sein.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Cinput_layer      : public Clayer
 {
   public:
                
   static const int     size;
                        
    Cinput_layer()      {
                          Clear();
                          Capacity(Cinput_layer::size);
                          for(int s=0; s<size; s++)
                          {
                            n = new Cneuron();
                            Add(n);
                          }
                        }
    ~Cinput_layer()     {};
 };
 const int Cinput_layer::size=1;


2.1.7 Output Layer Die Klasse wird ebenfalls von der Klasse CLayer abgeleitet, aber sie dient als unsere Karte, da hier "trainierte" Neuronen gespeichert werden. Der Funktordatenanteil der Neuronen in dieser Schicht entspricht einem Bild oder einer Karte Ihres typischen SOM. Seine Größe beträgt anfangs 10000 und wird um den gleichen Betrag erhöht, wenn neue Neuronen trainiert werden.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Coutput_layer      : public Clayer
 {
   public:
                
    int                  index;
    int                  size;
                        
    Coutput_layer()      {
                           index=0;
                           size=OUT_BUFFER;
                           Clear();
                           Capacity(size);
                           for(int s=0; s<size; s++)
                           {
                             n = new Cneuron();
                             Add(n);
                           }
                         };
                                                                        
    ~Coutput_layer()     {
                           ZeroMemory(index);
                           ZeroMemory(size);
                         };
 };


2.1.8 Network Die Klasse Cnetwork wird wie die Neuronenklasse ebenfalls von der HashMap-Vorlagenschnittstelle abgeleitet. Ihre Schlüssel- und Wertdatentypen sind die Klasse der Eingabeschicht und die Klasse der Ausgabeschicht. Sie verfügt über die meisten Funktionen (9), um nicht nur die Listengröße zu ermitteln, sondern auch die Neuronen in den jeweiligen Schichten abzurufen und zu aktualisieren.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Cnetwork           : public CHashMap<Cinput_layer*,Coutput_layer*>
 {
   public:
                
     Cinput_layer        *i;
     Coutput_layer       *o;
                        
     CKeyValuePair
     <
     Cinput_layer*,
     Coutput_layer*
     >                   *io;
                        
     Cneuron             *i_neuron;
     Cneuron             *o_neuron;
                        
     Cneuron             *best_neuron;
                        
     Cnetwork()          {
                           i = new Cinput_layer();
                           o = new Coutput_layer();
                           io = new CKeyValuePair<Cinput_layer*,Coutput_layer*>(i,o);
                           Add(io);
                                                                                
                           i_neuron = new Cneuron();
                           o_neuron = new Cneuron();
                                                                                
                           best_neuron = new Cneuron();
                         };
                                                                        
     ~Cnetwork()         {
                           delete i;
                           delete o;
                           delete io;
                           delete i_neuron;
                           delete o_neuron;
                           delete best_neuron;
                         };
                        
      virtual int        GetInputSize()
                         {
                           TryGetValue(i,o);
                           return(i.size);
                         };
                        
      virtual int        GetOutputIndex()
                         {
                           TryGetValue(i,o);
                           return(o.index);
                         };
                        
      virtual void       SetOutputIndex(const int Index)
                         {
                           TryGetValue(i,o);
                           o.index=Index;
                           TrySetValue(i,o);
                         };
                        
      virtual int        GetOutputSize()
                         {
                           TryGetValue(i,o);
                           return(o.size);
                         };
                        
      virtual void       SetOutputSize(const int Size)
                         {
                           TryGetValue(i,o);
                           o.size=Size;
                           o.Capacity(Size);
                           TrySetValue(i,o);
                         };
                        
      virtual void       GetInNeuron(const int NeuronIndex)
                         {
                           TryGetValue(i,o);
                           i.TryGetValue(NeuronIndex,i_neuron);
                         };
                        
      virtual void       GetOutNeuron(const int NeuronIndex)
                         {
                           TryGetValue(i,o);
                           o.TryGetValue(NeuronIndex,o_neuron);
                         };
                        
      virtual void       SetInNeuron(const int NeuronIndex)
                         {
                           i.TrySetValue(NeuronIndex,i_neuron);
                         };
                        
      virtual void       SetOutNeuron(const int NeuronIndex)
                         {
                           o.TrySetValue(NeuronIndex,o_neuron);
                         };
 };


2.1.9 Map ist die endgültige Dachklasse. Es ruft eine Instanz der Netzklasse auf und enthält weitere Variablen für das Training der Neuronen und die Ermittlung des am besten passenden Neurons für das Netz.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class Cmap
  {
    public:
                        
      Cnetwork               *network;
                        
      static const double     radius;
      static double           time;
                        
      double                  QE;       //proxy for Quantization Error
      double                  TE;       //proxy for Topological Error
                        
      datetime                refreshed;
                        
      bool                    initialised;
                        
      Cmap()                  {
                                network = new Cnetwork();
                                                                                        
                                initialised=false;
                                                                                        
                                time=0.0;
                                                                                        
                                QE=0.50;
                                TE=5000.0;
                                                                                        
                                refreshed=D'1970.01.05';
                               };
                                                                                
      ~Cmap()                  {
                                 ZeroMemory(initialised);
                                                                                        
                                 ZeroMemory(time);
                                                                                        
                                 ZeroMemory(QE);
                                 ZeroMemory(TE);
                                                                                        
                                 ZeroMemory(refreshed);
                               };
 };
 const double Cmap::radius=IN_RADIUS;
 double Cmap::time=10000/fmax(1.0,log(IN_RADIUS));

 


2.2 Topologie.

2.2.1 Neuronales Training ist der competitive learning (Lernwettbewerb), bei dem die Gewichte der vorhandenen Neuronen in der Ausgabeschicht angepasst und ein neues Trainerneuron hinzugefügt wird. Die Geschwindigkeit, mit der diese Gewichte angepasst werden, und vor allem die Anzahl der Wiederholungen, die zur Anpassung dieser Gewichte erforderlich sind, sind sehr empfindliche Parameter, die die Effizienz des Netzes bestimmen. Bei jeder Iteration der Anpassung der Gewichte wird ein neuer kleinerer Radius berechnet. Ich bezeichne diesen Radius als den functor-error (nicht zu verwechseln mit dem SOM Topological-error), aber die meisten bezeichnen ihn als den Nachbarschaftsradius, gemessen durch den euklidischen Abstand. Ich wähle "Fehler", da dies ein Parameter ist, der für bessere Netzwerkergebnisse minimiert werden muss. Je mehr Iterationen man durchführt, desto kleiner wird der Functor-Fehler.  Neben der Anzahl der Iterationen muss auch die Lernrate schrittweise von einer Zahl nahe Eins gegen Null reduziert werden.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CSignalKM::NetworkTrain(Cmap &Map,Cneuron &TrainNeuron)
  {
    Map.TE=0.0;

    int _iteration=0;
    double _training_rate=m_training_rate;

    int _err=0;
    double _functor_error=0.0;

    while(_iteration<m_training_iterations)
    {
      double _current_radius=GetTrainingRadius(Map,_iteration);

      for(int i=0; i<=Map.network.GetOutputIndex(); i++)
      {
        Map.network.GetOutNeuron(i);
        double _error = EuclideanFunctor(TrainNeuron,Map.network.o_neuron);

        if(_error<_current_radius)
        {
          _functor_error+=(_error);
          _err++;

          double _remapped_radius = GetRemappedRadius(_error, _current_radius);

          SetWeights(TrainNeuron,Map.network.o_neuron,_remapped_radius,_training_rate);

          Map.network.SetOutNeuron(i);
        }
      }

      _iteration++;
      _training_rate=_training_rate*exp(-(double)_iteration/m_training_iterations);
    }

    int
    _size=Map.network.GetOutputSize(),
    _index=Map.network.GetOutputIndex();
    Map.network.SetOutputIndex(_index+1);
    if(_index+1>=_size)
    {
      Map.network.SetOutputSize(_size+OUT_BUFFER);
    }

    Map.network.GetOutNeuron(_index+1);
    for(int w=0; w<IN_WIDTH; w++)
    {
      Map.network.o_neuron.fd.Set(w,TrainNeuron.fd.Get(w));
    }
    
    for(int l=0; l<OUT_LENGTH; l++)
    {
      Map.network.o_neuron.fr.Set(l,TrainNeuron.fr.Get(l));
    }

    Map.network.SetOutNeuron(_index+1);

    if(_err>0)
    {
      _functor_error/=_err;
      Map.TE=_functor_error*IN_RADIUS;
    }
  }


2.2.2 Der Topologischer Fehler ist ein Schlüsselattribut in Kohonen-Karten. Ich betrachte dies als ein Maß dafür, wie nahe die Ausgabeschicht ihrem langfristigen Ziel ist. Vergessen wir nicht, dass die Neuronen der Ausgabeschicht mit jedem Training an das wahre oder beabsichtigte Ergebnis angepasst werden. Die Antwort darauf ist, dass wir diesem Ziel näher kommen, wenn wir die Ausgabeschicht mehr bewahren.  Für die Zwecke dieses Artikels werde ich den Funktor-Fehler als Stellvertreter für ihn verwenden.


2.3 Quantisierung

2.3.1 Neuronen-Zuordnung ist ein Prozess, bei dem es darum geht, die Gewichte des Funktors zu finden, die am besten zu einem Neuron passen, für das nur Feed-Daten vorhanden sind. Dazu wird das Neuron in der Ausgabeschicht gefunden, das den kürzesten euklidischen Abstand zu dem Neuron aufweist, für das keine Funktordaten bekannt sind. Wie beim Training bezeichne ich diesen Abstand als feed-error. Auch hier gilt: Je kleiner unser Wert, desto zuverlässiger sollte das Netz sein.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CSignalKM::NetworkMapping(Cmap &Map,Cneuron *MapNeuron)
  {
    Map.QE=0.0;
    
    Map.network.best_neuron = new Cneuron();

    int _random_neuron=rand()%Map.network.GetOutputIndex();

    Map.network.GetInNeuron(0);
    Map.network.GetOutNeuron(_random_neuron);

    double _feed_error = EuclideanFeed(Map.network.i_neuron,Map.network.o_neuron);

    for(int i=0; i<Map.network.GetOutputIndex(); i++)
    {
      Map.network.GetOutNeuron(i);

      double _error = EuclideanFeed(Map.network.i_neuron,Map.network.o_neuron);

      if(_error < _feed_error)
      {
        for(int w=0; w<IN_WIDTH; w++)
        {
          Map.network.best_neuron.fd.Set(w,Map.network.o_neuron.fd.Get(w));
        }

        for(int l=0; l<OUT_LENGTH; l++)
        {
          Map.network.best_neuron.fr.Set(l,Map.network.o_neuron.fr.Get(l));
        }

        _feed_error = _error;
      }
    }

    Map.QE=_feed_error/IN_RADIUS;
}


2.3.2 Quantisierungsfehler ist ein weiteres kritisches Attribut in Kohonen-Karten, für das es meiner Meinung nach keine präzise Definition gibt. Meiner Meinung nach liegt das an dem Fehler bei der Übersetzung hochdimensionaler Daten in die niedrigdimensionale Ausgabe. In unserem Fall wäre es der Fehler bei der Umwandlung des Feeds in den Funktor. Für die Zwecke dieses Artikels werde ich den "feed-error" als Stellvertreter dafür verwenden.


 

3. Assemblieren mit dem MQL5 Wizard

3.1 Der Assistent für das Assemblieren ist einfach zu bedienen. Die einzige Warnung, die ich hier habe, ist, dass man zuerst mit großen Zeitrahmen testen sollte, da die idealen 10.000 Trainingsiterationen pro Balken einige Zeit dauern, wenn man über einen längeren Zeitraum trainiert.

wizard_1

 


 

4.  Testen im Strategietester

4.1 Die Standardeingaben für unsere Tests werden die Empfindlichkeit unseres Quantisierungsfehler-Proxys (QE) und des topologischen Fehler-Proxys (TE) untersuchen. Wir werden uns zwei Szenarien ansehen. Zunächst werden wir mit sehr konservativen Werten testen, wobei QE und TE bei 0,5 und 12,5 liegen; dann werden wir diese Eingaben bei 0,75 bzw. 25,0 testen .

criteria_1

Konservative Optionen;


kriterien_2

Aggressive Optionen.

 

 

Die Eingaben sind nicht sehr zahlreich. Es gibt die Option "training read", die bestimmt, ob vor der Initialisierung eine Trainingsdatei gelesen werden soll oder nicht. Fehlt diese Datei, wird der Experte nicht validieren. Außerdem gibt es die Funktion "training write", die, wie der Name schon sagt, festlegt, ob eine Lerndatei geschrieben werden soll, sobald der Experte seine Initialisierung aufhebt.  Das Training findet immer dann statt, wenn der Experte im Einsatz ist. Die Option, nur zu trainieren und nicht zu handeln, wird durch den Eingabeparameter 'training only' festgelegt. Die beiden anderen wichtigen Parameter für Kohonen-Karten sind die 'training rate‘ (auch als Lernrate bezeichnet) und die Trainingsiterationen. Je höher diese beiden Werte sind (die Trainingsrate ist auf 1,0 begrenzt), desto besser ist die zu erwartende Leistung, allerdings auf Kosten von Zeit und CPU-Ressourcen. 

Der Experte wurde für den V-förmigen Zeitraum von 2018.10.01 bis 2021.06.01 auf EURJPY trainiert und vom Enddatum des Trainings bis zum heutigen Datum vorwärts getestet.

Die konservative Option kam zu diesem Ergebnisbericht:

report_1

Und diese Kapitalkurve.

curve_1


Die aggressivere Option hatte jedoch diesen Ergebnisbericht:

report_2

 

Und diese Kapitalkurve:

curve_2

 

Natürlich sind weitere Tests und Feinabstimmungen in Bezug auf Risiko und Positionsgröße erforderlich, aber für ein System, das über einen so kurzen Zeitraum trainiert wird, ist es vielversprechend. Ein Vergleich der beiden obigen Szenarien zeigt jedoch, dass die konservativere Option nicht ausreichend belohnt wird, da ihr Sharpe-Ratio-Wert von 0,43 fast halb so hoch ist wie der Wert von 0,85 bei mehr Positionen. Hier ist vor dem Einsatz eine genauere Untersuchung erforderlich, und wie immer sollten neben der Anpassung des Feeds und der Funktordaten an den eigenen Handelsstil auch Vorabtests mit den Real-Ticks-Daten Ihres Brokers über längere Zeiträume durchgeführt werden, bevor der Einsatz erfolgt.

 

5 SCHLUSSFOLGERUNG

5.1 Der Der MQL5-Assistent ist eindeutig ein sehr agiles Werkzeug, wenn es darum geht, Handelssysteme in einem engen Zeitfenster zusammenzustellen. In diesem Artikel haben wir die Möglichkeit von Kohonen-Karten untersucht, die mehrdimensionale Daten von Preiszeitreihen in eine einzige Dimension übertragen, die von -1,0 bis 1,0 reicht. Dieser Ansatz ist zwar nicht üblich, aber er entspricht dem Wesen der Kohonen-Karten, die Komplexität zu reduzieren und die Entscheidungsfindung zu erleichtern. Wir haben dies auch getan, indem wir weiteren Code aus der MQL-Bibliothek wie Array-Listen und Hash-Maps vorgestellt haben. Ich hoffe, es hat Ihnen gefallen. Danke fürs Lesen.

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

Beigefügte Dateien |
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 15): Zugang zu Daten im Internet (I) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 15): Zugang zu Daten im Internet (I)
Wie kann man über den MetaTrader 5 auf Online-Daten zugreifen? Es gibt viele Webseiten und Orte im Internet, die eine riesige Menge an Informationen bieten. Sie müssen nur wissen, wo Sie suchen und wie Sie diese Informationen am besten nutzen können.
DoEasy. Steuerung (Teil 6): Paneel-Steuerung, automatische Größenanpassung des Containers an den inneren Inhalt DoEasy. Steuerung (Teil 6): Paneel-Steuerung, automatische Größenanpassung des Containers an den inneren Inhalt
In diesem Artikel werde ich meine Arbeit an dem WinForms-Objekt Panel fortsetzen und seine automatische Größenanpassung an die allgemeine Größe der Dock-Objekte, die sich innerhalb des Paneels befinden, implementieren. Außerdem werde ich die neuen Eigenschaften zum Objekt der Symbolbibliothek hinzufügen.
Neuronale Netze leicht gemacht (Teil 15): Datenclustering mit MQL5 Neuronale Netze leicht gemacht (Teil 15): Datenclustering mit MQL5
Wir fahren fort mit der Betrachtung der Clustermethode. In diesem Artikel werden wir eine neue CKmeans-Klasse erstellen, um eine der gängigsten k-means-Clustermethoden zu implementieren. Während der Tests gelang es dem Modell, etwa 500 Muster zu erkennen.
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 14): Hinzufügen des Volumens zum Preis (II) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 14): Hinzufügen des Volumens zum Preis (II)
Heute werden wir unserem EA weitere Ressourcen hinzufügen. Dieser interessante Artikel kann einige neue Ideen und Methoden zur Präsentation von Informationen liefern. Gleichzeitig kann es Ihnen helfen, kleinere Fehler in Ihren Projekten zu beheben.