Experimente mit neuronalen Netzen (Teil 1): Die Geometrie neu betrachten

Roman Poshtar | 11 August, 2022

Einführung

In diesem Artikel möchte ich über meine Experimente mit neuronalen Netzen berichten.  Nachdem ich eine große Menge an Informationen über MQL5 gelesen hatte, kam ich zu dem Schluss, dass die Theorie ausreichend ist. Es gibt viele gute Artikel, Bibliotheken und Quellcodes. Leider führen all diese Daten nicht zu einer logischen Schlussfolgerung - einem profitablen Handelssystem. Versuchen wir, das zu beheben.

Ich bin kein Experte auf diesem Gebiet und schon gar kein Schriftsteller oder Journalist, aber ich werde versuchen, meine Gedanken in einer zugänglichen Form auszudrücken, um meine Erfahrungen zu teilen.

Das Material ist in erster Linie für Anfänger wie mich gedacht.


Mein Verständnis. Die Grundlagen

Es wird allgemein angenommen, dass neuronale Netze gut darin sind, Muster zu erkennen, wobei die Daten, die dem neuronalen Netz zum Training übergeben werden, von größter Bedeutung sind. Ich werde mit dieser Annahme beginnen. Nehmen wir die Geometrie. Ich werde geometrische Formen auf ein neuronales Netz übertragen. Nehmen wir zunächst ein normales Perceptron, dessen Muster ich hier gefunden habe (МTC Сombo - Experte für MetaTrader 4). Bei der Durchführung von Tests beschloss ich, die Oszillatoren aufzugeben und MA zu verwenden. Tests mit Oszillatoren erbrachten keine guten Ergebnisse. Ich glaube, jeder kennt die so genannte Divergenz, wenn der Kurs steigt und der Oszillator sinkt. MA-Parameter sind näher am Preis selbst.


Formen und Linien

Die Basis besteht aus zwei gleitenden Durchschnitten mit den Parametern 1 und 24, die einfache Methode wird auf Close angewendet. Mit anderen Worten, die Idee ist, nicht nur die aktuellen Standorte der Indikatoren zu übermitteln, sondern auch die Zustände, in denen sie sich vor dem aktuellen befanden. In vielen Beispielen, die ich gesehen habe, wird der Preis direkt an das neuronale Netz weitergegeben, was ich für grundlegend falsch halte. 

Ich gebe alle Werte in Punkten an, was sehr wichtig ist, da diese Werte einen bestimmten Bereich haben, über den sie nicht hinausgehen können. Die Übergabe eines Preises an ein neuronales Netz hat keinen Sinn, da er z. B. 10 Jahre lang in verschiedenen Bereichen schwanken kann. Denken Sie auch daran, dass wir bei der Erstellung von Formen eine unterschiedliche Anzahl von Indikatorparametern verwenden können. Formen können komplex oder einfach sein. Im Folgenden sind einige mögliche Optionen aufgeführt. Sie können sich natürlich auch Ihre eigenen ausdenken.

 Form 1: Einfache Linien

 Die Abstände in Punkten auf der geschlossenen Kerze 1, 4, 7 und 10 zwischen MA 1 und MA 24.

Perzeptron 1

double perceptron1() 
  {
   double w1 = x1 - 100.0;
   double w2 = x2 - 100.0;
   double w3 = x3 - 100.0;
   double w4 = x4 - 100.0;
   
   double a1 = (ind_In1[1]-ind_In2[1])/Point();
   double a2 = (ind_In1[4]-ind_In2[4])/Point();
   double a3 = (ind_In1[7]-ind_In2[7])/Point();
   double a4 = (ind_In1[10]-ind_In2[10])/Point();
   
   return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4);
  }


Form 2: Einfache Linien

Abstände in Punkten zwischen den geschlossenen Kerzen 1-4, 4-7 und 7-10 der MA 1.

Perzeptron 2

double perceptron2() 
  {
   double w1 = y1 - 100.0;
   double w2 = y2 - 100.0;
   double w3 = y3 - 100.0;
   
   double a1 = (ind_In1[1]-ind_In1[4])/Point();
   double a2 = (ind_In1[4]-ind_In1[7])/Point();
   double a3 = (ind_In1[7]-ind_In1[10])/Point();
   
   return (w1 * a1 + w2 * a2 + w3 * a3);
  }


Form 3: Einfache Linien

Abstände in Punkten zwischen den geschlossenen Kerzen 1-4, 4-7 und 7-10 der MA 24.

Perzeptron 3

double perceptron3() 
  {
   double w1 = z1 - 100.0;
   double w2 = z2 - 100.0;
   double w3 = z3 - 100.0;
   
   double a1 = (ind_In2[1]-ind_In2[4])/Point();
   double a2 = (ind_In2[4]-ind_In2[7])/Point();
   double a3 = (ind_In2[7]-ind_In2[10])/Point();
   
   return (w1 * a1 + w2 * a2 + w3 * a3);
  }


Form 4: Schmetterling (Hülle)

Abstände in Punkten zwischen der geschlossenen Kerze 1-10 der MA 1. Und der Abstand in Punkten zwischen der geschlossenen Kerze 1-10 der MA 24.  Der Abstand in Punkten zwischen der Kerze 1 des MA 1 und der Kerze 10 des MA 24. Der Abstand in Punkten zwischen der Kerze 1 des MA 24 und der Kerze 10 des MA 1. Das Ergebnis ist ein Schmetterling.

Perzeptron 4

double perceptron4() 
  {
   double w1 = f1 - 100.0;
   double w2 = f2 - 100.0;
   double w3 = f3 - 100.0;
   double w4 = f4 - 100.0;
   
   double a1 = (ind_In1[1]-ind_In1[10])/Point();
   double a2 = (ind_In2[1]-ind_In2[10])/Point();
   double a3 = (ind_In1[1]-ind_In2[10])/Point();
   double a4 = (ind_In2[1]-ind_In1[10])/Point();
   
   return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4);
  }


Form 5: Viereck

Die Abstände in Punkten zwischen der geschlossenen Kerze 1-1, 10-10 zwischen den Indikatoren. Und der Abstand in Punkten zwischen 1-10 des MA 1 und der Abstand in Punkten zwischen 1-10 des Indikators MA 24. Das Ergebnis ist ein Viereck.

Perzeptron 5

double perceptron5() 
  {
   double w1 = c1 - 100.0;
   double w2 = c2 - 100.0;
   double w3 = c3 - 100.0;
   double w4 = c4 - 100.0;
   
   double a1 = (ind_In1[1]-ind_In1[10])/Point();
   double a2 = (ind_In2[1]-ind_In2[10])/Point();
   double a3 = (ind_In1[1]-ind_In2[1])/Point();
   double a4 = (ind_In1[10]-ind_In2[10])/Point();
   
   return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4);
  }


Form 6: Komplexe

Hier werde ich alle oben genannten Formen zu einer komplexen Form kombinieren.

Perzeptron 6

double perceptron6() 
  {
   double w1 = x1 - 100.0;
   double w2 = x2 - 100.0;
   double w3 = x3 - 100.0;
   double w4 = x4 - 100.0;  
   
   double w5 = y1 - 100.0;
   double w6 = y2 - 100.0;
   double w7 = y3 - 100.0;
   
   double w8 = z1 - 100.0;
   double w9 = z2 - 100.0;
   double w10 = z3 - 100.0;
  
   double w11 = f1 - 100.0;
   double w12 = f2 - 100.0;
   double w13 = f3 - 100.0;
   double w14 = f4 - 100.0;
   
   double a1 = (ind_In1[1]-ind_In2[1])/Point();
   double a2 = (ind_In1[4]-ind_In2[4])/Point();
   double a3 = (ind_In1[7]-ind_In2[7])/Point();
   double a4 = (ind_In1[10]-ind_In2[10])/Point();  
   
   double a5 = (ind_In1[1]-ind_In1[4])/Point();
   double a6 = (ind_In1[4]-ind_In1[7])/Point();
   double a7 = (ind_In1[7]-ind_In1[10])/Point();
   
   double a8 = (ind_In2[1]-ind_In2[4])/Point();
   double a9 = (ind_In2[4]-ind_In2[7])/Point();
   double a10 = (ind_In2[7]-ind_In2[10])/Point();
   
   double a11 = (ind_In1[1]-ind_In1[10])/Point();
   double a12 = (ind_In2[1]-ind_In2[10])/Point();
   double a13 = (ind_In1[1]-ind_In2[10])/Point();
   double a14 = (ind_In2[1]-ind_In1[10])/Point(); 
   
   return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4   +   w5 * a5 + w6 * a6 + w7 * a7   +   w8 * a8 + w9 * a9 + w10 * a10   +   w11 * a11 + w12 * a12 + w13 * a13 + w14 * a14);
  }


Winkel

Betrachten wir eine weitere interessante Methode zur Übermittlung von Kursdaten an ein Perzeptron - die Steigungswinkel von Indikatoren. Diese Daten können auch nicht über einen bestimmten Bereich hinausgehen, was für uns ganz passend ist, da wir eine bestimmte Vorlage übergeben wollen, genau wie im vorherigen Fall mit Formen und Linien. 

Ich bin auf viele Methoden zur Definition von Winkeln gestoßen, aber viele von ihnen hängen von der Skala des Preisdiagramms ab, was für mich nicht geeignet ist. Anstatt den Winkel zu berechnen, werde ich also den Tangens des Winkels berechnen, indem ich das Verhältnis zwischen der Anzahl der Punkte und der Anzahl der Stäbe verwende. tg(α) Winkeltangens ist ein Verhältnis zwischen dem gegenüberliegenden Schenkel a und dem angrenzenden Schenkel b.

ta

Es ist auch möglich, eine andere Anzahl von Indikatoren zu verwenden, komplexe Strukturen zu behandeln und eine andere Anzahl von Kerzen für die Analyse zu verwenden. Die Steigungswinkel werden in den Screenshots als nicht fixierte Linien dargestellt. Betrachten wir einige Beispiele.


Perzeptron t1. 3 Steigungswinkel des MA 1

MA 1 Steigungswinkel zwischen den Kerzen 1-4, zwischen den Kerzen 1-7, zwischen den Kerzen 1-10. 

Perzeptron t1

double perceptront1() 
  {
   double w1 = x1 - 100.0;
   double w2 = x2 - 100.0;
   double w3 = x3 - 100.0;
   
   double a1 = (ind_In1[1]-ind_In1[4])/4;
   double a2 = (ind_In1[1]-ind_In1[7])/7;
   double a3 = (ind_In1[1]-ind_In1[10])/10;

   return (w1 * a1 + w2 * a2 + w3 * a3);
  }
Perzeptron t2. 4 Steigungswinkel von MA 1 und MA 24

MA 1 Steigung zwischen den Kerzen 1-5, zwischen den Kerzen 1-10.  MA 24 Steigung zwischen den Kerzen 1-5, zwischen den Kerzen 1-10.

Perzeptron t2

double perceptront2() 
  {
   double w1 = x1 - 100.0;
   double w2 = x2 - 100.0;
   double w3 = x3 - 100.0;
   double w4 = x4 - 100.0;
   
   double a1 = (ind_In1[1]-ind_In1[5])/5;
   double a2 = (ind_In1[1]-ind_In1[10])/10;
   double a3 = (ind_In2[1]-ind_In2[5])/5;
   double a4 = (ind_In2[1]-ind_In2[10])/10;
   
   return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4);
  }


Perzeptron t3. 4 Steigungswinkel von MA 1 und MA 24 (mehr oder weniger komplexe Konstruktion als Beispiel)

Die Winkel sind mit einer Steigung zwischen MA 1 und MA 24 verbunden.

Perzeptron t3

double perceptront3() 
  {
   double w1 = x1 - 100.0;
   double w2 = x2 - 100.0;
   double w3 = x3 - 100.0;
   double w4 = x4 - 100.0;
   
   double a1 = (ind_In1[1]-ind_In1[10])/10;
   double a2 = (ind_In2[1]-ind_In1[4])/4;
   double a3 = (ind_In2[1]-ind_In1[7])/7;
   double a4 = (ind_In2[1]-ind_In1[10])/10;
   
   return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4);
  }


Perzeptron t4. 4 Steigungswinkel von MA 1 und MA 24 (mehr oder weniger komplexe Konstruktion als Beispiel)

Die Winkel sind mit einer Steigung zwischen MA 1 und MA 24 verbunden.

Perzeptron t4

double perceptront4() 
  {
   double w1 = x1 - 100.0;
   double w2 = x2 - 100.0;
   double w3 = x3 - 100.0;
   double w4 = x4 - 100.0;
   
   double a1 = (ind_In1[1]-ind_In1[10])/10;
   double a2 = (ind_In2[1]-ind_In1[10])/10;
   double a3 = (ind_In1[1]-ind_In1[10])/10;
   double a4 = (ind_In2[1]-ind_In2[10])/10;
   
   return (w1 * a1 + w2 * a2 + w3 * a3 + w4 * a4);
  }

Strategie

Ich habe mich entschieden, für das Training eine Gegentrend-Strategie zu verwenden, die ich im unten stehenden Code explizit angegeben habe. Um zu verkaufen, liegt der MA 1 der ersten Kerze über dem MA 24. Beim Kauf wird es umgekehrt gemacht. Dies ist notwendig, um eine klare Trennung von Kauf und Verkauf zu gewährleisten. Sie können aber auch das Gegenteil tun und einen Trend nutzen.

Sie können auch andere Indikatoren oder deren Werte verwenden, z. B. den TEMA-Indikator. Es ist unmöglich, die Kursentwicklung von 400 Punkten bei einem fünfstelligen Symbol vorherzusagen. Niemand weiß, wohin sich der Markt entwickeln wird. Um einen Test durchzuführen, habe ich daher einen festen Stop-Loss von 600 Punkten und einen Take-Profit von 60 Punkten für ein fünfstelliges Symbol gesetzt. Sie können die vorgefertigten EAs unten herunterladen. Werfen wir einen Blick auf das Ergebnis.

//SELL++++++++++++++++++++++++++++++++++++++++++++++++

if ((CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_SELL, EAComment)==0) && (ind_In1[1]>ind_In2[1]) && (perceptron1()<0) &&(SpreadS1<=MaxSpread)){//v1
  OpenSell(symbolS1.Name(), LotsXSell, TakeProfit, StopLoss, EAComment);
}

//BUY++++++++++++++++++++++++++++++++++++++++++++++++

if ((CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_BUY, EAComment)==0) && (ind_In1[1]<ind_In2[1]) && (perceptron1()>0) && (SpreadS1<=MaxSpread)){//v1
  OpenBuy(symbolS1.Name(), LotsXBuy, TakeProfit, StopLoss, EAComment);
}

Optimierung, Tests und Ressourcen

Wie wir wissen, erfordert die Optimierung neuronaler Netze erhebliche Rechenressourcen. Daher empfehle ich bei der Verwendung des Strategietesters zur Optimierung den Modus „Nur Eröffnungspreise“ mit der ausdrücklichen Angabe der Schlusskurse im Code selbst. Ansonsten ist das mit meinen eher bescheidenen Kapazitäten nicht machbar. Aber auch mit diesem Optimierungsmodus empfehle ich die Nutzung des Cloud-Netzwerkdienstes. Das Ziel der Optimierung ist es, bestimmte profitable Muster zu finden. Die Häufigkeit solcher Muster (die Zahl der profitablen Geschäfte) sollte viel höher sein als die der unprofitablen. Alles hängt vom StopLoss/TakeProfit-Verhältnis ab.

Mit Blick auf die Zukunft sollte ich sagen, dass ich 10 Optimierungen von jedem EA kontinuierlich durchgeführt habe. Es gibt viele Optimierungswerte, was im Modus des Genetischen Algorithmus zu Ergebnissen von etwa 10.000-15.000 pro Durchgang führt. Je mehr Durchgänge, desto größer ist die Chance, die gewünschten Werte für die Gewichtsverhältnisse zu finden. Dieses Problem sollte mit Hilfe von MQL5 gelöst werden. Ich möchte den Strategietester wirklich nicht aufgeben.

Im Gegensatz zu dem oben genannten Artikel (wo der Schritt gleich 1 ist), wurde der Schritt von 5 Optimierungswerten nicht zufällig gewählt. Bei den Experimenten habe ich festgestellt, dass dies zu einer größeren Streuung der Perzeptron-Gewichtsverhältnisse führt, was sich besser auf die Ergebnisse auswirkt.


Optimierung von EA 1 Perzeptron 1 Abbildung. Einfache Linien. (Ein Perzeptron mit einer Form).

     

    Optimierungs- und Vorwärtstestergebnisse.

    1 Perzeptron 1 Figur

    Wie Sie sehen können, sind die Ergebnisse alles andere als vielversprechend. Das beste Ergebnis ist 0,87. Es macht keinen Sinn, einen Vorwärtstest durchzuführen.


    Optimierung von EA 1 Perzeptron 4 Abbildung. (Ein Perzeptron mit vier Formen).

     

    Optimierungs- und Vorwärtstestergebnisse.

    1 Perzeptron 4 Abbildung

    Das Ergebnis ist dem vorherigen sehr ähnlich. Das beste Ergebnis ist 0,94.


    Optimierung von EA 4 Perzeptron 4 Abbildung. (Vier Perzeptrons mit vier verschiedenen Formen).

     Der Hauptcode sieht wie folgt aus:

    //SELL++++++++++++++++++++++++++++++++++++++++++++++++
    
    if ((CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_SELL, EAComment)==0) && (ind_In1[1]>ind_In2[1]) && (perceptron1()<0) && (perceptron2()<0) && (perceptron3()<0) && (perceptron4()<0) && (SpreadS1<=MaxSpread)){//v1
      OpenSell(symbolS1.Name(), LotsXSell, TakeProfit, StopLoss, EAComment);
    }
    
    //BUY++++++++++++++++++++++++++++++++++++++++++++++++
    
    if ((CalculatePositions(symbolS1.Name(), Magic, POSITION_TYPE_BUY, EAComment)==0) && (ind_In1[1]<ind_In2[1]) && (perceptron1()>0) && (perceptron2()>0) && (perceptron3()>0) && (perceptron4()>0) && (SpreadS1<=MaxSpread)){//v1
      OpenBuy(symbolS1.Name(), LotsXBuy, TakeProfit, StopLoss, EAComment);
    }

    Optimierungs- und Vorwärtstestergebnisse.

    4 Perzeptron 4 Abbildung

    Vorwärtstest vom 31.05.2021 bis 31.05.2022. Aus allen Ergebnissen sollte dasjenige ausgewählt werden, das den größten Gewinnfaktor aufweist, wobei das Maximum des komplexen Kriteriums 40-50 übersteigt.

    Test 1

    Test 2

     

    Optimierungs- und Vorwärtstestergebnisse.

    4 Perzeptron 4 Abbildung

    Das Ergebnis liegt vor. Das beste Ergebnis ist 32. Ändern wir das Datum vom 31.05.2021 auf den 31.05.2022 und führen einen Vorwärtstest durch. Von allen Ergebnissen sollten wir dasjenige auswählen, das den größten Gewinnfaktor bei einer Mindestanzahl von Geschäften von 10-20 aufweist.

    Test 1

    Test 2


    Optimierung von EA 4 Perzeptron 4 Tangens. (Vier Perzeptrons mit vier verschiedenen Winkeln).

    Optimierungs- und Vorwärtstestergebnisse.

    4 Perzeptron 4 Tangens

    Vorwärtstest vom 31.05.2021 auf den 31.05.2022. Von allen Ergebnissen sollte dasjenige ausgewählt werden, das den größten Gewinnfaktor aufweist, wobei das Maximum des komplexen Kriteriums über 20-40 liegt.

    Test 1

    Test 2

     

    Optimierungs- und Vorwärtstestergebnisse.

    4 Perzeptron 4 Tangens

    Das Ergebnis liegt vor. Das beste Ergebnis ist 32. Ich werde einen Vorwärtstest als Hausaufgabe hinterlassen. Ich denke, so wird es interessanter. Darüber hinaus ist festzustellen, dass die Anzahl der Positionen im Verhältnis zum Gewinnfaktor zugenommen hat.


     Im Laufe meiner Experimente bin ich auf mehrere Probleme gestoßen, die gelöst werden sollten.


    Schlussfolgerung

    Ich hoffe wirklich, dass meine Experimente Sie zu neuen Entdeckungen und schließlich zum Erfolg führen werden. Mein Ziel war es, eine fertige profitable Strategie zu erhalten. Das habe ich teilweise erreicht, indem ich gute Ergebnisse bei den Vorwärtsprüfungen erzielt habe. Es liegt jedoch noch viel Arbeit vor uns. Es ist an der Zeit, zu komplexeren Systemen überzugehen und dabei von den gewonnenen Erfahrungen zu profitieren. Außerdem sollten wir das, was wir haben, besser nutzen. Darüber und über vieles mehr wollen wir im zweiten Teil unserer Experimente sprechen. Verpassen Sie es nicht! Jetzt wird es noch spannender.