English 日本語
preview
Klassische Strategien neu interpretieren (Teil 12): EURUSD Ausbruchsstrategie

Klassische Strategien neu interpretieren (Teil 12): EURUSD Ausbruchsstrategie

MetaTrader 5Beispiele | 15 April 2025, 07:26
114 0
Gamuchirai Zororo Ndawana
Gamuchirai Zororo Ndawana

In diesem Artikel werden wir gemeinsam eine Handelsstrategie in MQL5 erstellen. Wir werden eine Ausbruchs-Handelsstrategie implementieren und diese iterativ verbessern, um ihr volles Potenzial auszuschöpfen. Lassen Sie uns einige der Spezifikationen unserer Strategie erörtern.

Wir werden uns auf das EURUSD-Paar konzentrieren und seine Bewegungen auf dem H1-Zeitrahmen handeln. Unsere Ausbruchsstrategie wird zunächst die aktuellen Höchst- und Tiefstkurse des EURUSD-Paares erfassen. Im Laufe der Zeit werden wir abwarten, ob sich die Kurse vollständig außerhalb des Kanals öffnen und schließen, der durch den anfänglichen Höchst- und Tiefstkurs entstanden ist.

Wenn dies geschieht, hat unsere Handelsstrategie eine Tendenz gefunden, dass sich die Märkte wahrscheinlich weiterhin in eine bestimmte Richtung bewegen werden. Dies ist nicht der Punkt, an dem unsere Positionen eingegeben werden. Wir werden unsere Positionen einnehmen, wenn sich unser Vorurteil bestätigt hat. Sobald sich die Kurse vollständig öffnen und über dem Extrempunkt der Kerze schließen, die aus unserem anfänglichen Kanal ausgebrochen ist, eröffnen wir Kaufspositionen, wenn wir uns über dem Kanal befinden, und andernfalls Verkaufspositionen.

Bisher hat das System, das wir angegeben haben, zu viele Handelsgeschäfte eröffnet. Wir müssen andere Metriken für Stärke oder Schwäche angeben, um die unrentablen Geschäfte herauszufiltern, die wir möglicherweise eingehen könnten. Der gleitende Durchschnitt kann uns helfen, Markttrends schnell zu erkennen.

Wir werden unser System so gestalten, dass wir zunächst die aktuellen Preise auf dem Markt, auf dem wir uns befinden, überwachen und dann beobachten, in welche Richtung der Preis aus dem Kanal ausbricht und ob dieser Ausbruch durch künftige Preisaktionen unterstützt wird. Wenn der von uns beobachtete Ausbruch mit der Kursentwicklung nach dem Ausbruch übereinstimmt, verwenden wir unsere gleitenden Durchschnitte, um den Zeitpunkt der Auftragsausführung zu bestimmen.

Wir bevorzugen Kaufspositionen, wenn der sich schnell bewegende Durchschnitt über dem langsamen liegt, und das Gegenteil gilt für unsere Verkaufspositionen. Alle unsere Trades werden aktiv mit Hilfe des Indikators Average True Range aktualisiert, um unsere Stop-Loss und Take-Profit zu berechnen.

Wir werden unsere Handelsstrategie für den Zeitraum zwischen dem 1. Januar 2020 und dem 30. November 2024 im H1-Zeitrahmen testen.

Unsere technischen Indikatoren werden wie folgt aufgebaut sein:

  1. Schneller gleitender Durchschnitt: Exponentieller gleitender 5-Perioden-Durchschnitt, bezogen auf den Schlusskurs.
  2. Langsamer gleitender Durchschnitt: Exponentieller gleitender Durchschnitt über 60 Perioden, angewandt auf den Schlusskurs.
  3. Average True Range: ATR-Indikator mit einer Periodenlänge von 14.
Unsere Handelsanwendung funktioniert nach den grundlegenden Handelsregeln. Zu Beginn, wenn unser System zum ersten Mal geladen wird, markieren wir einfach Hoch und Tief der vorherigen Kerze und warten dann, bis der Preis auf einer der beiden Seiten ausbricht. Solange dies nicht der Fall ist, bleibt unser Bias bei 0, und es werden keine Bestätigungen oder Handelsgeschäfte platziert.


    Abb. 1: Der anfängliche Zustand der Anwendung unserer Ausbruchsstrategie.

    Nach einiger Zeit werden sich die Kurse schließlich öffnen und außerhalb des Kanals schließen. Dieser Extrempunkt wird unsere Tendenz sein, die Seite, der die Märkte unserer Meinung nach folgen werden. Unsere Tendenz wird sich bestätigen, wenn die Kurse anschließend unter der Tendenz schließen. Andernfalls werden wir keine Geschäfte abschließen.


    Abb. 2: Unsere Handelsanwendung hat eine Marktverzerrung festgestellt.

    Wenn die Preisniveaus unsere Tendenz bestätigen, haben wir das Vertrauen, eine Position auf dem Markt zu eröffnen. Unsere Strategie wird zunächst trendfolgend sein. Wenn die Kurse also über den Kanal ausbrechen, werden wir nach Kaufgelegenheiten Ausschau halten.

      Abb. 3: Unsere Positionen werden eröffnet, nachdem unsere Tendenz bestätigt worden ist.


      Erste Schritte in MQL5

      Unsere Handelsanwendung setzt sich aus Handelslogik und fundamentalen technischen Analysekonzepten zusammen. Lassen Sie uns die wichtigsten Elemente des Codes hervorheben.

      Systemteil
      Verwendungszweck
      Konstanten und Parameter
      Wir werden bestimmte Aspekte unseres Handelsalgorithmus festlegen, um die Konsistenz über alle unsere Tests hinweg zu gewährleisten, z. B. die Zeiträume der gleitenden Durchschnitte, die Losgröße und den Abstand von unserem Stop-Loss und Take-Profit.
      Globale Variablen
      Diese Variablen werden in verschiedenen Teilen unseres Codes verwendet, und es ist wichtig, dass wir jedes Mal, wenn wir sie verwenden, auf denselben Wert verweisen. Einige der globalen Variablen in unserer Anwendung umfassen den Höchst- und Tiefstwert des Kanals, die Richtung, der der Markt unserer Meinung (Bias) nach folgen wird und andere technische Indikatorwerte.

      Wir müssen auch andere wichtige Variablen in unserer Handelsanwendung definieren, die uns dabei helfen, den Zustand des Marktes im Auge zu behalten. Machen wir uns mit den wichtigsten davon vertraut.

      Variable
      Verwendungszweck
      Bias
      Der Bias-Parameter symbolisiert die Richtung, in die sich die Preise zu bewegen scheinen. Er kann den Wert 1 annehmen, wenn der Trend nach oben gerichtet ist, und den Wert -1, wenn der Trend nach unten gerichtet ist. Andernfalls wird er auf 0 gesetzt.
      Gleitende Durchschnitte
      Der schnelle gleitenden Durchschnitt (ma_f) und der langsame gleitenden Durchschnitt (ma_s) bestimmen den Trend. Wenn ma_f[0] > ma_s[0] und der Preis (c) über dem sich schnell bewegenden Durchschnitt liegt, wird ein Kauf eröffnet. Andernfalls, wenn ma_f[0] < ma_s[0] und der Kurs unter dem sich langsam bewegenden Durchschnitt liegt, wird ein Verkauf eröffnet.
      Ausbruch
      Wenn die Kanalgrenze (oben oder unten) durchbrochen wird, wird die Bewegungsrichtung (Bias) festgelegt.
      Ausbruchsniveaus
      Das Ausbruchsniveau gibt Aufschluss darüber, in welche Richtung sich die Märkte unserer Meinung nach in Zukunft entwickeln werden. Wenn die Märkte über die obere Grenze ausbrechen, wird unsere Stimmung von einen Aufwärtstrend ausgehen.
      Signalbestätigung
      Unsere Handelsgeschäfte werden nicht ohne Signalbestätigung platziert. Das Signal wird bestätigt, wenn der Markt nach dem Ausbruch seine Richtung beibehält. Wenn die Bestätigung ausbleibt, kann die Position angepasst oder geschlossen werden.
      Verwaltung der Aufträge Welche Handelsgeschäfte wir platzieren werden, hängt vom Trend ab, die wir derzeit am Markt beobachten. Im Falle eines Aufwärtstrends (Bias == 1) wird der Befehl gesendet: Trade.Buy(vol, Symbol(), ask, channel_low, 0, "Volatility Doctor AI"); andernfalls wird im Falle eines Abwärtstrends (bias == -1) der Auftrag gesendet: Trade.Sell(vol, Symbol(), bid, channel_high, 0, "Volatility Doctor AI");
       Stop Loss  Wird bei Käufen zunächst auf „channel_low“ und bei Verkäufen auf „channel_high“ gesetzt und in Zukunft anhand des ATR-Werts aktualisiert.

      Nun, da wir einen konzeptionellen Überblick über die beweglichen Teile unserer Strategie haben, können wir mit dem gemeinsamen Aufbau unserer Handelsstrategie beginnen. Zunächst müssen wir die Einzelheiten unserer Handelsanwendung festlegen.

      //+------------------------------------------------------------------+
      //|                                                MTF Channel 2.mq5 |
      //|                                        Gamuchirai Zororo Ndawana |
      //|                          https://www.mql5.com/en/gamuchiraindawa |
      //+------------------------------------------------------------------+
      #property copyright "Gamuchirai Zororo Ndawana"
      #property link      "https://www.mql5.com/en/gamuchiraindawa"
      #property version   "1.00"
      

      Binden wir nun die Handelsbibliothek ein:

      //+------------------------------------------------------------------+
      //| Library                                                          |
      //+------------------------------------------------------------------+
      #include  <Trade/Trade.mqh>
      CTrade Trade;
      

      Definieren wir nun die Konstanten für unsere Handelsanwendung, wie z.B. die Perioden einiger unserer technischen Indikatoren.

      //+------------------------------------------------------------------+
      //| Constants                                                        |
      //+------------------------------------------------------------------+
      const  int ma_f_period = 5; //Slow MA
      const  int ma_s_period = 60; //Slow MA
      

      Lassen Sie uns nun Eingaben definieren, die unser Endnutzer einstellen kann. Da wir unsere technischen Indikatoren fixiert halten, wird der Endnutzer nicht mit zahlreichen Parametern überfordert.

      //+------------------------------------------------------------------+
      //| Inputs                                                           |
      //+------------------------------------------------------------------+
      input  group "Money Management"
      input int lot_multiple = 5; //Lot Multiple
      input int atr_multiple = 5; //ATR Multiple
      

      Globale Variablen werden wir in den meisten Teilen unseres Programms verwenden.

      //+------------------------------------------------------------------+
      //| Global varaibles                                                 |
      //+------------------------------------------------------------------+
      double channel_high = 0;
      double channel_low  = 0;
      double o,h,l,c;
      int    bias = 0;
      double bias_level = 0;
      int    confirmation = 0;
      double vol,bid,ask,initial_sl;
      int    atr_handler,ma_fast,ma_slow;
      double atr[],ma_f[],ma_s[];
      double bo_h,bo_l;
      

      Wenn unsere Handelsanwendung zum ersten Mal geladen wird, rufen wir eine spezielle Funktion auf, um unsere technischen Indikatoren zu laden und andere notwendige Marktdaten für uns vorzubereiten.

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //---
         setup();
      //---
         return(INIT_SUCCEEDED);
        }
      

      Wenn wir unseren Expert Advisor nicht mehr verwenden, sollten wir die nicht mehr benötigten Ressourcen freigeben.

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //---
            IndicatorRelease(atr_handler);
            IndicatorRelease(ma_fast);
            IndicatorRelease(ma_slow);
        }

      Sobald wir aktualisierte Preise erhalten, aktualisieren wir unsere globalen Variablen und suchen dann nach neuen Handelsmöglichkeiten.

      //+------------------------------------------------------------------+
      //| Expert tick function                                             |
      //+------------------------------------------------------------------+
      void OnTick()
        {
      //--- If we have positions open
         if(PositionsTotal() > 0)
            manage_setup();
      
      //--- Keep track of time
         static datetime timestamp;
         datetime time = iTime(Symbol(),PERIOD_CURRENT,0);
         if(timestamp != time)
           {
            //--- Time Stamp
            timestamp = time;
            if(PositionsTotal() == 0)
               find_setup();
           }
        }
      

      Die folgende Funktion ist für das Laden unserer technischen Indikatoren und das Abrufen von Marktdaten zuständig.

      //+---------------------------------------------------------------+
      //| Load our technical indicators and market data                 |
      //+---------------------------------------------------------------+
      void setup(void)
        {
         channel_high = iHigh(Symbol(),PERIOD_M30,1);
         channel_low  = iLow(Symbol(),PERIOD_M30,1);
         vol = lot_multiple * SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
         ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high);
         ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low);
         atr_handler = iATR(Symbol(),PERIOD_CURRENT,14);
         ma_fast     = iMA(Symbol(),PERIOD_CURRENT,ma_f_period,0,MODE_EMA,PRICE_CLOSE);
         ma_slow     = iMA(Symbol(),PERIOD_CURRENT,ma_s_period,0,MODE_EMA,PRICE_CLOSE);
        }
      

      Wenn unsere Strategie zum ersten Mal geladen wird, markieren wir die aktuellen Höchst- und Tiefstpreise, die auf dem Markt angeboten werden. Auf diese Weise können alle zukünftigen Preise, die wir beobachten werden, im Kontext beobachtet werden, wir können sie mit den anfänglichen Preisniveaus vergleichen, die wir gesehen haben, als wir ankamen.

      //+---------------------------------------------------------------+
      //| Update channel                                                |
      //+---------------------------------------------------------------+
      void update_channel(double new_high, double new_low)
        {
         channel_high = new_high;
         channel_low  = new_low;
         ObjectDelete(0,"Channel High");
         ObjectDelete(0,"Channel Low");
         ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high);
         ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low);
        }
      

      Wenn wir offene Positionen haben, müssen wir unsere Stop-Loss und Take-Profit entsprechend aktualisieren. Wir passen unsere Risikoeinstellungen anhand eines Vielfachen der Average True Range an, sodass unsere Risikoeinstellungen mit den aktuellen Volatilitätsniveaus am Markt in Beziehung stehen.

      //+---------------------------------------------------------------+
      //| Manage setup                                                  |
      //+---------------------------------------------------------------+
      void manage_setup(void)
        {
         bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
         ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
         CopyBuffer(atr_handler,0,0,1,atr);
         Print("Managing Position");
      
         if(PositionSelect(Symbol()))
           {
            Print("Position Found");
            initial_sl = PositionGetDouble(POSITION_SL);
           }
      
         if(bias == 1)
           {
            Print("Position Buy");
            double new_sl = (ask - (atr[0] * atr_multiple));
            Print("Initial: ",initial_sl,"\nNew: ",new_sl);
            if(initial_sl < new_sl)
              {
               Trade.PositionModify(Symbol(),new_sl,0);
               Print("DONE");
              }
           }
      
         if(bias == -1)
           {
            Print("Position Sell");
            double new_sl = (bid + (atr[0] * atr_multiple));
            Print("Initial: ",initial_sl,"\nNew: ",new_sl);
            if(initial_sl > new_sl)
              {
               Trade.PositionModify(Symbol(),new_sl,0);
               Print("DONE");
              }
           }
      
        }
      

      Wenn wir keine offenen Positionen haben, folgen wir den Regeln, die wir zuvor beschrieben haben, um Handelsmöglichkeiten zu identifizieren. Erinnern wir uns daran, dass wir eine starke Kursbewegung beobachten wollen, die aus dem anfänglichen Kanal ausbricht, in dem sich der Kurs befindet. Danach werden wir genügend Vertrauen gewinnen, um uns auf den Handel einzulassen, wenn sich die Preise weiterhin in dieselbe Richtung bewegen und den offenen Kanal, den sie gerade gebildet haben, nicht durchbrechen.

      //+---------------------------------------------------------------+
      //| Find Setup                                                    |
      //+---------------------------------------------------------------+
      void find_setup(void)
        {
      //--- We are updating the system
         o = iOpen(Symbol(),PERIOD_CURRENT,1);
         h = iHigh(Symbol(),PERIOD_CURRENT,1);
         l = iLow(Symbol(),PERIOD_CURRENT,1);
         c = iClose(Symbol(),PERIOD_CURRENT,1);
         bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
         ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
         CopyBuffer(atr_handler,0,0,1,atr);
         CopyBuffer(ma_fast,0,0,1,ma_f);
         CopyBuffer(ma_slow,0,0,1,ma_s);
      
      //--- If we have no market bias
         if(bias == 0)
           {
            //--- Our bias is bullish
            if
            (
               (o > channel_high) &&
               (h > channel_high) &&
               (l > channel_high) &&
               (c > channel_high)
            )
              {
               bias = 1;
               bias_level = h;
               bo_h = h;
               bo_l = l;
               mark_bias(h);
              }
      
            //--- Our bias is bearish
            if
            (
               (o < channel_low) &&
               (h < channel_low) &&
               (l < channel_low) &&
               (c < channel_low)
            )
              {
               bias = -1;
               bias_level = l;
               bo_h = h;
               bo_l = l;
               mark_bias(l);
              }
           }
      
      //--- Is our bias valid?
         if(bias != 0)
           {
      
            //--- Our bearish bias has been violated
            if
            (
               (o > channel_high) &&
               (h > channel_high) &&
               (l > channel_high) &&
               (c > channel_high) &&
               (bias == -1)
            )
              {
               forget_bias();
              }
            //--- Our bullish bias has been violated
            if
            (
               (o < channel_low) &&
               (h < channel_low) &&
               (l < channel_low) &&
               (c < channel_low) &&
               (bias == 1)
            )
              {
               forget_bias();
              }
      
            //--- Our bullish bias has been violated
            if
            (
               ((o < channel_high) && (c > channel_low))
            )
              {
               forget_bias();
              }
      
            //--- Check if we have confirmation
            if((confirmation == 0) && (bias != 0))
              {
               //--- Check if we are above the bias level
               if
               (
                  (o > bias_level) &&
                  (h > bias_level) &&
                  (l > bias_level) &&
                  (c > bias_level) &&
                  (bias == 1)
               )
                 {
                  confirmation = 1;
                 }
      
               //--- Check if we are below the bias level
               if
               (
                  (o < bias_level) &&
                  (h < bias_level) &&
                  (l < bias_level) &&
                  (c < bias_level) &&
                  (bias == -1)
               )
                 {
                  confirmation = 1;
                 }
              }
           }
      
      //--- Check if our confirmation is still valid
         if(confirmation == 1)
           {
            //--- Our bias is bullish
            if(bias == 1)
              {
               //--- Confirmation is lost if we fall beneath the breakout level
               if
               (
                  (o < bias_level) &&
                  (h < bias_level) &&
                  (l < bias_level) &&
                  (c < bias_level)
               )
                 {
                  confirmation = 0;
                 }
              }
      
            //--- Our bias is bearish
            if(bias == -1)
              {
               //--- Confirmation is lost if we rise above the breakout level
               if
               (
                  (o > bias_level) &&
                  (h > bias_level) &&
                  (l > bias_level) &&
                  (c > bias_level)
               )
                 {
                  confirmation = 0;
                 }
              }
           }
      
      //--- Do we have a setup?
         if((confirmation == 1) && (bias == 1))
           {
            if(ma_f[0] > ma_s[0])
              {
               if(c > ma_f[0])
                 {
                  Trade.Buy(vol,Symbol(),ask,channel_low,0,"Volatility Doctor AI");
                  initial_sl = channel_low;
                 }
              }
           }
      
         if((confirmation == 1) && (bias == -1))
           {
            if(ma_f[0] < ma_s[0])
              {
               if(c < ma_s[0])
                 {
                  Trade.Sell(vol,Symbol(),bid,channel_high,0,"Volatility Doctor AI");
                  initial_sl = channel_high;
                 }
              }
           }
         Comment("O: ",o,"\nH: ",h,"\nL: ",l,"\nC:",c,"\nC H: ",channel_high,"\nC L:",channel_low,"\nBias: ",bias,"\nBias Level: ",bias_level,"\nConfirmation: ",confirmation,"\nMA F: ",ma_f[0],"\nMA S: ",ma_s[0]);
        }
      

      Wenn die Kurse aus dem ursprünglichen Kanal ausbrechen, markieren wir das extreme Kursniveau, das durch die Kerze, die aus dem Kanal ausgebrochen ist, entstanden ist. Dieses extreme Niveau ist unser Bias.

      //+---------------------------------------------------------------+
      //| Mark our bias levels                                          |
      //+---------------------------------------------------------------+
      void mark_bias(double f_level)
        {
         ObjectCreate(0,"Bias",OBJ_HLINE,0,0,f_level);the
        }
      

      Fallen die Kurse wieder in den Kanal zurück, nachdem sie zuvor ausgebrochen sind, betrachten wir den alten Kanal als ungültig und aktualisieren die neue Position des Kanals auf die durch die Ausbruchskerze entstandenen Niveaus.

      //+---------------------------------------------------------------+
      //| Forget our bias levels                                        |
      //+---------------------------------------------------------------+
      void forget_bias()
        {
         update_channel(bo_h,bo_l);
         bias = 0;
         bias_level = 0;
         confirmation = 0;
         ObjectDelete(0,"Bias");
        }
      //+------------------------------------------------------------------+
      

      Wir sind jetzt bereit für den Backtest der Ausbruchs-Handelsstrategie. Ich habe die Anwendung „MTF Channel 2“ genannt, was für „Multiple Time Frame Channel“ steht. Ich habe das EURUSD-Symbol auf dem H1-Zeitrahmen ausgewählt. Unsere Testzeitpunkte sind die gleichen wie die zuvor genannten Zeitpunkte. Der Leser wird feststellen, dass diese 3 speziellen Einstellungen für alle 3 Tests gleich waren.

      Unsere Anfangseinstellungen

      Abb. 4: Die erste Gruppe von Einstellungen, die für unseren ersten Backtest verwendet wurde.

      Dies sind nicht alle Parameter, die wir festgelegt haben. Wir haben zufällige Verzögerungseinstellungen gewählt, um Echtzeit-Handelsszenarien zu simulieren, wobei die erlebte Latenz variieren kann. Wir haben uns auch dafür entschieden, den Test auf der Grundlage echter Ticks zu modellieren, um zu versuchen, eine realitätsnahe Erfahrung des realen Handels zu erhalten.

      Unsere zweiten Einstellungen

      Abb. 5: Zweite Gruppe von Einstellungen, die für den Test unserer Strategie ausgewählt wurden.

      Wir werden die Einstellungen unseres Expert Advisors so anpassen, dass sie für alle Tests, die wir durchführen werden, gleich sind. Wenn wir diese Einstellungen beibehalten, können wir durch die Auswahl besserer Handelsregeln die Ursache für die Profitabilität isolieren.

      Unsere Systemeinstellungen

      Abb. 6: Unsere Einstellungen zur Geldverwaltung.

      Lassen Sie uns unsere Strategie in Aktion sehen. In Abb. 7 unten sehen wir auf der rechten Seite des Bildschirms die internen Variablen, die unsere Anwendung für ihre Entscheidungen verwendet. Beachten Sie, dass alle unsere Handelsgeschäfte nur platziert werden, wenn die „confirmation“ auf 1 gesetzt ist.

      Unser System in Aktion

      Abb. 7: Der Backtest unserer Handelsstrategie für das EURUSD-Paar.

      Leider müssen wir feststellen, dass wir mit unserer Strategie Geld verloren haben. Dies ist ein Zeichen dafür, dass es noch Raum für Verbesserungen gibt.

      Unser Kontostand im Laufe der Zeit.

      Abb. 8: Anzeige des Diagramms, zu dem unsere Backtest führte.

      Lassen Sie uns mehr Details über den soeben durchgeführten Test erfahren. Wir können deutlich sehen, dass unsere Strategie insgesamt 53 Handelsgeschäfte identifiziert hat, von denen 70 % unrentabel waren. Unsere Sharpe Ratio ist negativ. Dies sind schlechte Leistungskennzahlen.

      Auf der anderen Seite ist unser durchschnittlicher Gewinn größer als unser durchschnittlicher Verlust, das ist eine gute Nachricht. Lassen Sie uns sehen, wie wir besser werden können. Wir wollen mehr Kontrolle über unsere Brutto- und Durchschnittsverluste ausüben und gleichzeitig unseren durchschnittlichen Gewinn und den Anteil der profitablen Geschäfte maximieren.

      Detaillierte Analyse von System 1

      Abb. 9: Die Details zu unserem Backtest.

      Verbesserung der ersten Ergebnisse

      Als ich den Backtest beobachtete, war es frustrierend zu sehen, wie der Expert Advisor immer wieder denselben Fehler machte. Die meisten unserer Verluste sind entstanden, weil wir auf bedeutungslose Kursschwankungen gesetzt haben, die zufällig alle unsere Bedingungen erfüllten. Die einzige Lösung besteht darin, bessere Bedingungen zu wählen, die auf natürliche Weise schwache und starke Bewegungen auf dem Markt unterscheiden können.

      Eine Möglichkeit besteht darin, die Performance von EUR und USD mit einer gemeinsamen Benchmark zu vergleichen. Hierfür können wir GBP verwenden. Wir werden vergleichen, wie sich das EURGBP- und das GBPUSD-Paar entwickeln, bevor wir eine Position eröffnen. Das heißt, wenn wir auf unserem Diagramm beobachten, dass sich der EURUSD in einem starken Aufwärtstrend befindet, würden wir auch gerne sehen, dass sich der EURGBP im gleichen Trend bewegt, und der GBPUSD sollte hoffentlich ebenfalls in einem Aufwärtstrend sein.

      Mit anderen Worten: Wenn die EURUSD-Kurse uns den Eindruck vermitteln, dass der Euro teurer wird als der Dollar, dann werden wir nur dann Vertrauen gewinnen, wenn wir auch beobachten, dass der Euro gegenüber dem Britischen Pfund an Wert gewinnt, während der Dollar gleichzeitig gegenüber dem Britischen Pfund billiger wird. Dieser dreieckige Wechselkurs wird uns hoffentlich helfen, falsche Ausbrüche zu erkennen. Unsere Überlegung ist, dass Schwankungen, die alle 3 Märkte gleichzeitig betreffen, wirklich starke Bewegungen sein können, von denen wir profitieren können.

      Wir werden einige Codezeilen hinzufügen, um die ursprüngliche Handelsstrategie, die wir bisher entwickelt haben, zu ändern. Um die angedachten Änderungen zu implementieren, werden wir zunächst neue globale Variablen erstellen, um die Preise der Paare EURGBP und GBPUSD zu verfolgen. Wir müssen auch technische Indikatoren auf unsere beiden anderen Märkte anwenden, damit wir die Trends auf diesen Märkten im Auge behalten können.

      //+------------------------------------------------------------------+
      //| Global variables                                                 |
      //+------------------------------------------------------------------+
      double channel_high = 0;
      double channel_low  = 0;
      double o,h,l,c;
      int    bias = 0;
      double bias_level = 0;
      int    confirmation = 0;
      double vol,bid,ask,initial_sl;
      int    atr_handler,ma_fast,ma_slow;
      double atr[],ma_f[],ma_s[];
      double bo_h,bo_l;
      int    last_trade_state,current_state;
      int    eurgbp_willr, gbpusd_willr;
      string symbols[] = {"EURGBP","GBPUSD"};
      

      Wenn unser Expert Advisor zum ersten Mal geladen wird, müssen wir ein paar zusätzliche Schritte durchführen, um die Kursentwicklung unserer Benchmark-Symbole zu verfolgen. Diese Aktualisierungen werden in der Setup-Funktion implementiert.

      //+---------------------------------------------------------------+
      //| Load our technical indicators and market data                 |
      //+---------------------------------------------------------------+
      void setup(void)
        {
      //--- Select the symbols we need
         SymbolSelect("EURGBP",true);
         SymbolSelect("GBPUSD",true);
      //--- Reset our last trade state
         last_trade_state = 0;
      //--- Mark the current high and low
         channel_high = iHigh("EURUSD",PERIOD_M30,1);
         channel_low  = iLow("EURUSD",PERIOD_M30,1);
         ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high);
         ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low);
      //--- Our trading volums
         vol = lot_multiple * SymbolInfoDouble("EURUSD",SYMBOL_VOLUME_MIN);
      //--- Our technical indicators
         atr_handler  = iATR("EURUSD",PERIOD_CURRENT,14);
         eurgbp_willr = iWPR(symbols[0],PERIOD_CURRENT,wpr_period);
         gbpusd_willr = iWPR(symbols[1],PERIOD_CURRENT,wpr_period);
         ma_fast      = iMA("EURUSD",PERIOD_CURRENT,ma_f_period,0,MODE_EMA,PRICE_CLOSE);
         ma_slow      = iMA("EURUSD",PERIOD_CURRENT,ma_s_period,0,MODE_EMA,PRICE_CLOSE);
        }
      

      Wenn unsere Handelsanwendung nicht mehr in Gebrauch ist, werden wir einige zusätzliche technische Indikatoren veröffentlichen.

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //---
         IndicatorRelease(eurgbp_willr);
         IndicatorRelease(gbpusd_willr);
         IndicatorRelease(atr_handler);
         IndicatorRelease(ma_fast);
         IndicatorRelease(ma_slow);
        }
      

      Unsere OnTick-Funktion bleibt unverändert. Die Funktionen, die er aufruft, werden jedoch geändert. Erstens müssen wir jedes Mal, wenn wir unseren Kanal aktualisieren, 3 Kanäle in den Märkten, die wir verfolgen, aktualisieren. Eine für den EURUSD, die zweite für den EURGBP und die letzte für den GBPUSD.

      //+---------------------------------------------------------------+
      //| Update channel                                                |
      //+---------------------------------------------------------------+
      void update_channel(double new_high, double new_low)
        {
         channel_high = new_high;
         channel_low  = new_low;
         ObjectDelete(0,"Channel High");
         ObjectDelete(0,"Channel Low");
         ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high);
         ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low);
        }
      

      Der größte Teil des Programms ist gleich geblieben. Die wichtigste Änderung, die wir vorgenommen haben, besteht darin, dass unsere Handelsanwendung nun 2 andere Märkte überprüfen muss, bevor sie sich für einen Handel entscheidet. Wenn unsere Fundamentaldaten uns zuversichtlich stimmen, dass der Ausbruch, den wir beim EURUSD beobachten, von echter Stärke getragen wird, dann werden wir die Position eingehen. Diese Aktualisierungen werden in der Suchfunktion berücksichtigt.

      Sie werden auch feststellen, dass die Funktion eine neue Funktion aufruft, die wir in der vorherigen Version der Anwendung der Ausbruchsstrategie nicht definiert haben. Die zusätzliche Bestätigungsfunktion überprüft die 2 Benchmark-Märkte auf unsere fundamentalen Handelsbedingungen.

      //+---------------------------------------------------------------+
      //| Find Setup                                                    |
      //+---------------------------------------------------------------+
      void find_setup(void)
        {
      //--- I have omitted code pieces that were unchanged
      //--- Do we have a setup?
         if((confirmation == 1) && (bias == 1) && (current_state != last_trade_state))
           {
            if(ma_f[0] > ma_s[0])
              {
               if(c > ma_f[0])
                 {
                  if(additional_confirmation(1))
                    {
                     Trade.Buy(vol,"EURUSD",ask,channel_low,0,"Volatility Doctor");
                     initial_sl = channel_low;
                     last_trade_state = 1;
                    }
                 }
              }
           }
      
         if((confirmation == 1) && (bias == -1)  && (current_state != last_trade_state))
           {
            if(ma_f[0] < ma_s[0])
              {
               if(c < ma_s[0])
                 {
                  if(additional_confirmation(-1))
                    {
                     Trade.Sell(vol,"EURUSD",bid,channel_high,0,"Volatility Doctor");
                     initial_sl = channel_high;
                     last_trade_state = -1;
                    }
                 }
              }
           }
      }

      Diese Funktion soll uns dabei helfen, das Marktrauschen von der wahren Stärke zu unterscheiden. Indem wir nach Bestätigungen in anderen verwandten Märkten suchen, hoffen wir, immer die bestmöglichen Handelsgeschäfte auszuwählen.

      //+---------------------------------------------------------------+
      //| Check for true strength                                       |
      //+---------------------------------------------------------------+
      bool additional_confirmation(int flag)
        {
      //--- Do we have additional confirmation from our benchmark pairs?
      
      //--- Record the average change in the EURGBP and GBPUSD Market
         vector eurgbp_willr_f = vector::Zeros(1);
         vector gbpusd_willr_f = vector::Zeros(1);
      
         eurgbp_willr_f.CopyIndicatorBuffer(eurgbp_willr,0,0,1);
         gbpusd_willr_f.CopyIndicatorBuffer(gbpusd_willr,0,0,1);
      
         if((flag == 1) && (eurgbp_willr_f[0] > -50) && (gbpusd_willr_f[0] < -50))
            return(true);
         if((flag == -1) && (eurgbp_willr_f[0] < -50) && (gbpusd_willr_f[0] > -50))
            return(true);
      
         Print("EURGBP WPR: ",eurgbp_willr_f[0],"\nGBPUSD WPR: ",gbpusd_willr_f[0]);
         return(false);
        }
      

      Diese Version unserer Anwendung wird den Titel „MTF EURUSD Channel“ tragen. Die erste Version, die wir erstellt haben, war allgemeiner gehalten und konnte leicht für den Handel mit jedem anderen Symbol in unserem Terminal verwendet werden. Diese Version verwendet jedoch die Paare EURGBP und GBPUSD als Benchmarks und ist daher spezieller und nur für den Handel mit dem EURUSD-Paar gedacht. Der Leser wird feststellen, dass unsere Testbedingungen mit denen des ersten Tests identisch sind. Wir werden diesen Backtest mit dem gleichen Zeitrahmen und über die gleichen Zeiträume wie beim ersten Test durchführen, also vom 1. Januar 2020 bis zum 30. November 2024.

      Unsere zweite Charge EA zum Testen

      Abb. 10: Die erste Reihe von Einstellungen für unseren Backtest der EURUSD Channel Break out Strategie.

      Wenn Sie dem hier vorgestellten Setup folgen wollen, sollten Sie beachten, dass die Einstellung der Option „Modellierung“ auf „Jeder Tick basierend auf realen Ticks“ je nach Internetverbindung zeitaufwändig sein kann, da das MT5-Terminal umfangreiche Daten von Ihrem Broker anfordert, um den Markt so realistisch wie möglich zu modellieren. Seien Sie also nicht beunruhigt, wenn der Vorgang mehrere Minuten dauern kann, und schalten Sie Ihren Computer nicht mitten im Vorgang aus.

      Unsere zweiten Eingaben für unsere Handelsanwendung.

      Abb. 11: Die zweite Gruppe von Einstellungen muss identisch mit den Einstellungen sein, die wir im ersten Test verwendet haben.

      Die Verwendung eines Lot-Multiplikators von 1 bedeutet, dass alle meine Handelsgeschäfte mit einer minimalen Losgröße platziert werden. Wenn es uns gelingt, unser System bei minimaler Losgröße rentabel zu machen, dann wird uns die Erhöhung des Multiplikators der Losgröße gute Dienste leisten. Wenn unser System jedoch bei einem minimalen Lot nicht profitabel ist, können wir durch eine Erhöhung der Losgröße nichts gewinnen.

      Unsere Parametereinstellungen

      Abb. 12: Die Parameter, mit denen wir das Verhalten unserer Anwendung steuern werden.

      Jetzt können wir sehen, wie unser Handelssystem mit historischen Daten funktioniert. Beachten Sie, dass diese Version unseres Systems 3 Märkte gleichzeitig überwacht. Zunächst werden wir immer das EURUSD-Paar im Auge behalten, damit wir unseren Trend daraus ableiten können.

      Unser System in Aktion auf dem EURUSD

      Abb. 13: Unser System in Aktion auf dem EURUSD-Paar.

      Unsere Positionen können nur eröffnet werden, wenn wir beobachten, dass die Paare EURGBP und GBPUSD in entgegengesetzte Richtungen tendieren, wie in Abb. 14 und 15 unten dargestellt. Wir werden den Trend der beiden Märkte anhand der Williams Percent Range beurteilen. Wenn der WPR über der 50er-Marke liegt, beurteilen wird das als einen Aufwärtstrend.

      Unser erstes Benchmark-Paar

      Abb. 14: Unser erstes Bestätigungspaar, der GBPUSD

      In diesem Fall haben wir eine Handelsmöglichkeit gefunden, den EURUSD zu kaufen. Wir haben diese Gelegenheit erkannt, weil die WPR-Werte der beiden Märkte auf entgegengesetzten Seiten der 50er-Marke lagen. Auf dieses Ungleichgewicht werden wahrscheinlich volatile Marktbedingungen folgen, ideal für jede Ausbruchsstrategie.

      Unser zweites Benchmark-Paar.

      Abb. 15: Unser zweites Bestätigungspaar-Paar.

      Die folgende Abbildung 9 zeigt, wie sich der Saldo unseres simulierten Handelskontos im Laufe der Zeit verändert.  Unser Ziel ist es, genau zu verstehen, warum unsere Strategie scheitert, damit wir versuchen können, ihre Schwächen zu verbessern.

      Die Veränderungen des Kontostandes im Laufe der Zeit

      Abb. 16: Darstellung des Kontostands im Zeitverlauf.

      Leider haben die Änderungen, die wir an unserem System vorgenommen haben, die Rentabilität unserer Handelsanwendung verringert. Unser durchschnittlicher Verlust und unser durchschnittlicher Gewinn stiegen um die gleichen Beträge. Und der Anteil der gewinnbringenden Geschäfte ging leicht zurück. 

      Detaillierte Analyse von System 2.

      Abb. 17: Detaillierte Ergebnisse aus unserem Rückentest.

      Letzter Versuch, sich zu verbessern

      Wir haben es versäumt, uns dort zu verbessern, wo es am meisten zählt: bei der Rentabilität. Anstatt zu versuchen, dem Markt unsere Ansichten aufzuzwingen, werden wir stattdessen dem Computer erlauben, zu lernen, wie er die gleitenden Durchschnitte besser nutzen kann, als wir es können. Unsere Ansichten darüber, wie man effektiv handelt, sind bis zu einem gewissen Grad voreingenommen.

      Wenn wir andererseits unserem Computer erlauben, die Beziehung zwischen dem Schlusskurs und dem gleitenden Durchschnitt zu erlernen, kann unser Computer seine eigenen Handelsregeln erstellen und auf der Grundlage dessen handeln, was er als Nächstes erwartet, im Gegensatz zu der reaktiven Form des Handels, die wir bisher praktiziert haben.

      Für den Anfang habe ich ein Skript erstellt, mit dem wir historische Marktdaten extrahieren können. Ziehen Sie das Skript einfach auf den Markt, auf dem Sie handeln möchten, damit wir loslegen können. Das Skript holt die Marktdaten für Sie ab, und es holt auch die beiden gleitenden Durchschnitte, die wir für unsere Strategie benötigen, im selben Format, das wir in unserer Handelsanwendung verwenden.

      //+------------------------------------------------------------------+
      //|                                                      ProjectName |
      //|                                      Copyright 2020, CompanyName |
      //|                                       http://www.companyname.net |
      //+------------------------------------------------------------------+
      #property copyright "Gamuchirai Zororo Ndawana"
      #property link      "https://www.mql5.com/en/users/gamuchiraindawa"
      #property version   "1.00"
      #property script_show_inputs
      
      //+------------------------------------------------------------------+
      //| Script Inputs                                                    |
      //+------------------------------------------------------------------+
      input int size = 100000; //How much data should we fetch?
      
      //+------------------------------------------------------------------+
      //| Global variables                                                 |
      //+------------------------------------------------------------------+
      int    ma_f_handler,ma_s_handler;
      double ma_f_reading[],ma_s_reading[];
      
      //+------------------------------------------------------------------+
      //| On start function                                                |
      //+------------------------------------------------------------------+
      void OnStart()
        {
      //--- Load indicator
         ma_s_handler  = iMA(Symbol(),PERIOD_CURRENT,60,0,MODE_EMA,PRICE_CLOSE);
         ma_f_handler  = iMA(Symbol(),PERIOD_CURRENT,5,0,MODE_EMA,PRICE_CLOSE);
      
      //--- Load the indicator values
         CopyBuffer(ma_f_handler,0,0,size,ma_f_reading);
         CopyBuffer(ma_s_handler,0,0,size,ma_s_reading);
      
         ArraySetAsSeries(ma_f_reading,true);
         ArraySetAsSeries(ma_s_reading,true);
      
      //--- File name
         string file_name = "Market Data " + Symbol() +" MA Cross" +  " As Series.csv";
      
      //--- Write to file
         int file_handle=FileOpen(file_name,FILE_WRITE|FILE_ANSI|FILE_CSV,",");
      
         for(int i= size;i>=0;i--)
           {
            if(i == size)
              {
               FileWrite(file_handle,"Time","Open","High","Low","Close","MA 5","MA 60");
              }
      
            else
              {
               FileWrite(file_handle,iTime(Symbol(),PERIOD_CURRENT,i),
                         iOpen(Symbol(),PERIOD_CURRENT,i),
                         iHigh(Symbol(),PERIOD_CURRENT,i),
                         iLow(Symbol(),PERIOD_CURRENT,i),
                         iClose(Symbol(),PERIOD_CURRENT,i),
                         ma_f_reading[i],
                         ma_s_reading[i]
                        );
              }
           }
      //--- Close the file
         FileClose(file_handle);
      
        }
      //+------------------------------------------------------------------+
      

      Analysieren der Daten in Python

      Jetzt, da Sie Ihre Marktdaten im CSV-Format haben, können wir mit dem Aufbau eines KI-Modells beginnen, das uns hoffentlich helfen wird, falsche Ausbrüche vorherzusagen und sie zu vermeiden.

      import pandas as pd
      import numpy  as np
      from   sklearn.model_selection import TimeSeriesSplit,cross_val_score
      from   sklearn.linear_model    import Ridge
      from sklearn.metrics import mean_squared_error
      import matplotlib.pyplot as plt
      import seaborn as sns

      Lesen Sie die Marktdaten ein, die wir zuvor extrahiert haben. Achten Sie auf die Spalte „Zeit“ in meinem Datenrahmen, und beachten Sie, dass der letzte Eintrag auf den 18. April 2019 datiert ist. Dies geschieht absichtlich. Es sei daran erinnert, dass die Starttermine für die vorherigen Tests beide am 1. Januar 2020 begannen. Das heißt, wir machen uns nichts vor, wenn wir dem Modell alle Antworten auf unseren Test geben.

      #Define the forecast horizon
      look_ahead          = 24
      #Read in the data
      data                = pd.read_csv('Market Data EURUSD MA Cross As Series.csv')
      #Drop the last 4 years
      data                =  data.iloc[:(-24 * 365 * 4),:]
      data.reset_index(drop=True,inplace=True)
      #Label the data
      data['Target']      = data['Close'].shift(-look_ahead)
      data['MA 5 Target']      = data['MA 5'].shift(-look_ahead)
      data['MA 5 Close Target']      = data['Target'] - data['MA 5 Target']
      data['MA 60 Target']      = data['MA 60'].shift(-look_ahead)
      data['MA 60 Close Target']      = data['Target'] - data['MA 60 Target']
      data.dropna(inplace=True)
      data.reset_index(drop=True,inplace=True)
      data


      Abb. 18: Unsere historischen Marktdaten.

      Testen wir, ob auf dem EURUSD-Markt die gleitenden Durchschnitte immer noch besser geeignet sind, den Kurs selbst vorherzusagen. Um unsere Hypothese zu testen, werden wir 30 identische neuronale Netze trainieren, um 3 Ziele nacheinander vorherzusagen. Zunächst werden wir den zukünftigen Kurs, den 5-Perioden-Durchschnitt und den 60-Perioden-Durchschnitt vorhersagen. Alle Ziele werden 24 Schritte in die Zukunft projiziert. Zunächst werden wir unsere Genauigkeit bei der direkten Preisvorhersage erfassen.

      #Classical error
      classical_error = []
      epochs = 1000
      for i in np.arange(0,30):
        model = MLPRegressor(hidden_layer_sizes=(10,4),max_iter=epochs,early_stopping=False,solver='lbfgs')
        classical_error.append(np.mean(np.abs(cross_val_score(model,data.loc[:,['Open','High','Low','Close']],data.loc[:,'Target'],cv=tscv,scoring='neg_mean_squared_error'))))

      Als Nächstes werden wir unsere Genauigkeit bei der Vorhersage des gleitenden 5-Perioden-Durchschnitts ermitteln.

      #MA Cross Over error
      ma_5_error = []
      for i in np.arange(0,30):
        model = MLPRegressor(hidden_layer_sizes=(10,4),max_iter=epochs,early_stopping=False,solver='lbfgs')
        ma_5_error.append(np.mean(np.abs(cross_val_score(model,data.loc[:,['Open','High','Low','Close','MA 5']],data.loc[:,'MA 5 Target'],cv=tscv,scoring='neg_mean_squared_error'))))

      Zum Schluss werden wir unsere Genauigkeit bei der Vorhersage des gleitenden 60-Perioden-Durchschnitts aufzeichnen.

      #New error
      ma_60_error = []
      for i in np.arange(0,30):
        model = MLPRegressor(hidden_layer_sizes=(10,4),max_iter=10000,early_stopping=False,solver='lbfgs')
        ma_60_error.append(np.mean(np.abs(cross_val_score(model,data.loc[:,['Open','High','Low','Close','MA 60']],data.loc[:,'MA 60 Target'],cv=tscv,scoring='neg_mean_squared_error'))))

      Wenn wir unsere Ergebnisse aufzeichnen. Wie in Abb. 12 zu sehen ist, verursachte die Vorhersage des gleitenden 60-Perioden-Durchschnitts den größten Fehler in unserem System, während die Vorhersage des gleitenden 5-Perioden-Durchschnitts weniger Fehler verursachte als die direkte Vorhersage des Preises.

      plt.plot(classical_error)
      plt.plot(ma_5_error)
      plt.plot(ma_60_error)
      plt.legend(['OHLC','MA 5 ','MA 60'])
      plt.axhline(np.mean(classical_error),color='blue',linestyle='--')
      plt.axhline(np.mean(ma_5_error),color='orange',linestyle='--')
      plt.axhline(np.mean(ma_60_error),color='green',linestyle='--')
      plt.grid()
      plt.ylabel('Cross Validated Error')
      plt.xlabel('Iteration')
      plt.title('Comparing Different The Error Associated With Different Targets')
      plt.show()

      Abb. 19: Visualisierung des Fehlers, der mit verschiedenen Zielen verbunden ist.

      Lassen Sie uns nun versuchen, ein Modell für unsere Handelsanwendung zu exportieren. Importieren wir die benötigten Bibliotheken,

      import onnx
      from skl2onnx import convert_sklearn
      from skl2onnx.common.data_types import FloatTensorType
      from sklearn.neural_network import MLPRegressor

      Geben Sie die benötigten Modelle an. Ich werde 2 Modelle für diese Aufgabe verwenden. Da der gleitende Durchschnitt der kurzen Periode leicht vorherzusagen ist, werde ich ein einfaches Ridge-Modell verwenden, um ihn vorherzusagen. Unser gleitender 60-Perioden-Durchschnitt erwies sich jedoch als Herausforderung. Daher werde ich ein neuronales Netz verwenden, um den langfristigen gleitenden Durchschnitt vorherzusagen.

      ma_5_model = Ridge()
      ma_5_model.fit(data[['Open','High','Low','Close','MA 5']],data['MA 5 Target'])
      ma_5_height_model = Ridge()
      ma_5_height_model.fit(data[['Open','High','Low','Close','MA 5']],data['MA 5 Close Target'])
      ma_60_model = Ridge()
      ma_60_model.fit(data[['Open','High','Low','Close','MA 60']],data['MA 60 Target'])
      ma_60_height_model = Ridge()
      ma_60_height_model.fit(data[['Open','High','Low','Close','MA 60']],data['MA 60 Close Target'])

      Bereiten Sie den Export nach ONNX vor.

      initial_type = [('float_input', FloatTensorType([1, 5]))]
      ma_5_onx = convert_sklearn(ma_5_model, initial_types=initial_type, target_opset=12 )
      ma_5_height_onx = convert_sklearn(ma_5_height_model, initial_types=initial_type, target_opset=12 )
      ma_60_height_onx = convert_sklearn(ma_60_height_model, initial_types=initial_type, target_opset=12 )
      ma_60_onx = convert_sklearn(ma_60_model, initial_types=initial_type, target_opset=12 )

      Speichern im ONNX-Format.

      onnx.save(ma_5_onx,'eurchf_ma_5_model.onnx')
      onnx.save(ma_60_onx,'eurchf_ma_60_model.onnx')
      onnx.save(ma_5_height_onx,'eurusd_ma_5_height_model.onnx')
      onnx.save(ma_60_height_onx,'eurusd_ma_60_height_model.onnx')

      Letzte Aktualisierungen in MQL5

      Wenden wir unsere neuen Modelle an, um zu sehen, ob sie uns helfen können, falsche Ausbrüche auf dem Markt herauszufiltern. Die erste Aktualisierung, die wir vornehmen müssen, ist das Importieren der ONNX-Modelle, die wir gerade erstellt haben.

      //+------------------------------------------------------------------+
      //|                                                MTF Channel 2.mq5 |
      //|                                        Gamuchirai Zororo Ndawana |
      //|                          https://www.mql5.com/en/gamuchiraindawa |
      //+------------------------------------------------------------------+
      #property copyright "Gamuchirai Zororo Ndawana"
      #property link      "https://www.mql5.com/en/gamuchiraindawa"
      #property version   "1.00"
      
      //+------------------------------------------------------------------+
      //| ONNX Resources                                                   |
      //+------------------------------------------------------------------+
      #resource "\\Files\\eurusd_ma_5_model.onnx"         as const uchar eurusd_ma_5_buffer[];
      #resource "\\Files\\eurusd_ma_60_model.onnx"        as const uchar eurusd_ma_60_buffer[];
      #resource "\\Files\\eurusd_ma_5_height_model.onnx"  as const uchar eurusd_ma_5_height_buffer[];
      #resource "\\Files\\eurusd_ma_60_height_model.onnx" as const uchar eurusd_ma_60_height_buffer[];

      Als Nächstes müssen wir einige neue Variablen erstellen, die mit unseren Modellen verbunden sind.

      //+------------------------------------------------------------------+
      //| Global varaibles                                                 |
      //+------------------------------------------------------------------+
      int     bias = 0;
      int     state = 0;
      int     confirmation = 0;
      int     last_cross_over_state = 0;
      int     atr_handler,ma_fast,ma_slow;
      int     last_trade_state,current_state;
      long    ma_5_model;
      long    ma_60_model;
      long    ma_5_height_model;
      long    ma_60_height_model;
      double  channel_high = 0;
      double  channel_low  = 0;
      double  o,h,l,c;
      double  bias_level = 0;
      double  vol,bid,ask,initial_sl;
      double  atr[],ma_f[],ma_s[];
      double  bo_h,bo_l;
      vectorf ma_5_forecast = vectorf::Zeros(1);
      vectorf ma_60_forecast = vectorf::Zeros(1);
      vectorf ma_5_height_forecast = vectorf::Zeros(1);
      vectorf ma_60_height_forecast = vectorf::Zeros(1);
      

      Wir müssen die Initialisierungsroutine so erweitern, dass sie nun unsere ONNX-Modelle für uns einrichtet.

      //+---------------------------------------------------------------+
      //| Load our technical indicators and market data                 |
      //+---------------------------------------------------------------+
      void setup(void)
        {
      //--- Reset our last trade state
         last_trade_state = 0;
      //--- Mark the current high and low
         channel_high = iHigh("EURUSD",PERIOD_M30,1);
         channel_low  = iLow("EURUSD",PERIOD_M30,1);
         ObjectCreate(0,"Channel High",OBJ_HLINE,0,0,channel_high);
         ObjectCreate(0,"Channel Low",OBJ_HLINE,0,0,channel_low);
      //--- Our trading volums
         vol = lot_multiple * SymbolInfoDouble("EURUSD",SYMBOL_VOLUME_MIN);
      //--- Our technical indicators
         atr_handler  = iATR("EURUSD",PERIOD_CURRENT,14);
         ma_fast      = iMA("EURUSD",PERIOD_CURRENT,ma_f_period,0,MODE_EMA,PRICE_CLOSE);
         ma_slow      = iMA("EURUSD",PERIOD_CURRENT,ma_s_period,0,MODE_EMA,PRICE_CLOSE);
      //--- Setup our ONNX models
      //--- Define our ONNX model
         ulong input_shape [] = {1,5};
         ulong output_shape [] = {1,1};
      
      //--- Create the model
         ma_5_model = OnnxCreateFromBuffer(eurusd_ma_5_buffer,ONNX_DEFAULT);
         ma_60_model = OnnxCreateFromBuffer(eurusd_ma_60_buffer,ONNX_DEFAULT);
         ma_5_height_model = OnnxCreateFromBuffer(eurusd_ma_5_height_buffer,ONNX_DEFAULT);
         ma_60_height_model = OnnxCreateFromBuffer(eurusd_ma_60_height_buffer,ONNX_DEFAULT);
      
      //--- Store our models in a list
         long onnx_models[] = {ma_5_model,ma_5_height_model,ma_60_model,ma_60_height_model};
      
      //--- Loop over the models and set them up
         for(int i = 0; i < 4; i++)
           {
            if(onnx_models[i] == INVALID_HANDLE)
              {
               Comment("Failed to load AI module correctly: Invalid handle");
              }
      
            //--- Validate I/O
            if(!OnnxSetInputShape(onnx_models[i],0,input_shape))
              {
               Comment("Failed to set input shape correctly:  Wrong input shape ",GetLastError()," Actual shape: ",OnnxGetInputCount(ma_5_model));
              }
      
            if(!OnnxSetOutputShape(onnx_models[i],0,output_shape))
              {
               Comment("Failed to load AI module correctly: Wrong output shape ",GetLastError()," Actual shape: ",OnnxGetOutputCount(ma_5_model));
              }
           }
        }
      

      Wenn unser System nicht mehr in Gebrauch ist, sollten wir die nicht mehr benötigten Ressourcen freigeben.

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //--- Free the resources we don't need
         IndicatorRelease(atr_handler);
         IndicatorRelease(ma_fast);
         IndicatorRelease(ma_slow);
         OnnxRelease(ma_5_model);
         OnnxRelease(ma_5_height_model);
         OnnxRelease(ma_60_model);
         OnnxRelease(ma_60_height_model);
        }

      Wenn wir aktualisierte Preise erhalten, besteht der einzige große Unterschied darin, dass wir auch versuchen, eine Prognose von unseren KI-Modellen zu erhalten.

      //+------------------------------------------------------------------+
      //| Expert tick function                                             |
      //+------------------------------------------------------------------+
      void OnTick()
        {
      //--- Keep track of time
         static datetime timestamp;
         datetime time = iTime(Symbol(),PERIOD_CURRENT,0);
         if(timestamp != time)
           {
            //--- Time Stamp
            timestamp = time;
            //--- Update system variables
            update();
            //--- Make a new prediction
            model_predict();
            if(PositionsTotal() == 0)
              {
               state = 0;
               find_setup();
              }
           }
      
      //--- If we have positions open
         if(PositionsTotal() > 0)
            manage_setup();
        }
      
      

      Wir müssen die Funktion definieren, die für das Abrufen einer Vorhersage aus unseren ONNX-Modellen in MQL5 verantwortlich ist.

      //+------------------------------------------------------------------+
      //| Get a prediction from our model                                  |
      //+------------------------------------------------------------------+
      void model_predict(void)
        {
         //--- Moving average inputs
         float  a = (float) ma_f[0];
         float  b = (float) ma_s[0];
      
         //--- Price quotes
         float op = (float) iOpen("EURUSD",PERIOD_H1,0);
         float hi = (float) iHigh("EURUSD",PERIOD_H1,0);
         float lo = (float) iLow("EURUSD",PERIOD_H1,0);
         float cl = (float) iClose("EURUSD",PERIOD_H1,0);
      
         //--- ONNX inputs
         vectorf fast_inputs = {op,hi,lo,cl,a};
         vectorf slow_inputs = {op,hi,lo,cl,b};
      
         Print("Fast inputs: ",fast_inputs);
         Print("Slow inputs: ",slow_inputs);
      
         //--- Inference
         OnnxRun(ma_5_model,ONNX_DATA_TYPE_FLOAT,fast_inputs,ma_5_forecast);
         OnnxRun(ma_5_height_model,ONNX_DATA_TYPE_FLOAT,fast_inputs,ma_5_height_forecast);
         OnnxRun(ma_60_model,ONNX_DEFAULT,slow_inputs,ma_60_forecast);
         OnnxRun(ma_60_height_model,ONNX_DATA_TYPE_FLOAT,fast_inputs,ma_60_height_forecast);
        }
      

      Die letzte Änderung, die wir vorgenommen haben, wirkt sich darauf aus, wie unsere Strategie ihre Handelsgeschäfte auswählt. Anstatt einfach drauflos zu handeln, platziert unsere Strategie ihre Handelsgeschäfte jetzt auf der Grundlage der Beziehung, die sie zwischen dem Preis und dem gleitenden Durchschnitt gelernt hat. Unsere Handelsanwendung verfügt nun über die Flexibilität, auch dann zu kaufen und zu verkaufen, wenn dies gegen die von uns angenommene Trend auf dem Markt verstößt.

      Beachten Sie, dass eine neue Funktion aufgerufen wird, nämlich „Valid Setup“. Diese Funktion gibt einfach „true“ zurück, wenn unsere Ausbruchsbedingungen erfüllt sind.

      //+---------------------------------------------------------------+ //| Find a setup | //+---------------------------------------------------------------+ void find_setup(void) { //--- I have skipped parts of the code that remained the same    if(valid_setup())      {       //--- Both models are forecasting rising prices       if((c < (ma_60_forecast[0] + ma_60_height_forecast[0])) && (c < (ma_5_forecast[0] + ma_5_height_forecast[0])))         {          if(last_trade_state != 1)            {             Trade.Buy(vol,"EURUSD",ask,0,0,"Volatility Doctor");             initial_sl = channel_low;             last_trade_state = 1;             last_cross_over_state = current_state;            }         }       //--- Both models are forecasting falling prices       if((c > (ma_60_forecast[0] + ma_60_height_forecast[0])) && (c > (ma_5_forecast[0] + ma_5_height_forecast[0])))         {          if(last_trade_state != -1)            {             Trade.Sell(vol,"EURUSD",bid,0,0,"Volatility Doctor");             initial_sl = channel_high;             last_trade_state = -1;             last_cross_over_state = current_state;            }         }      }

      Prüfen, ob es einen Ausbruch aus dem Kanal gibt. Wenn ja, gibt die Funktion true zurück, andernfalls false.

      //+---------------------------------------------------------------+
      //| Do we have a valid setup?                                     |
      //+---------------------------------------------------------------+
      bool valid_setup(void)
        {
         return(((confirmation == 1) && (bias == -1)  && (current_state != last_cross_over_state)) || ((confirmation == 1) && (bias == 1) && (current_state != last_cross_over_state)));
        }
      

      Ich denke, Sie sind inzwischen mit den Einstellungen vertraut, die wir für unseren Backtest festlegen werden. Wir erinnern daran, dass es wichtig ist, diese Einstellungen konsistent zu halten, damit wir die Änderungen in der Rentabilität isolieren können, die mit den Änderungen an unseren Handelsregeln verbunden sind.

      Unsere Eingabeeinstellungen

      Abb. 20: Einige der Einstellungen, die wir für den Backtest unserer letzten Handelsstrategie verwenden werden.

      Es sei daran erinnert, dass unser Modell nur bis 2019 trainiert wurde, unser Test aber 2020l beginnt. Wir simulieren also genau das, was passiert wäre, wenn wir dieses System in der Vergangenheit entworfen hätten.

      Unsere zweite Charge von Einstellungen für den dritten Test

      Abb. 21: Die zweite Gruppe von Einstellungen werden wir für Backtests unserer letzten Handelsstrategie verwenden.

      Auch hier sind die Einstellungen für alle drei Tests gleich.

      Unsere Anwendungseinstellungen

      Abb. 22: Die Einstellungen, die wir zur Steuerung unserer Anwendung im letzten Test verwenden werden.

      Wir können nun unsere modellbasierte Anwendung für den Ausbruchshandel beim EURUSD in Aktion sehen. Es sei daran erinnert, dass diese Daten den Modellen beim Training nicht angezeigt wurden.

      Unser AI-System in Aktion

      Abb. 23: Unsere endgültige modellbasierte Version der Ausbruchsstrategie in Aktion.

      Aus der nachstehenden Abbildung 23 geht hervor, dass es uns endlich gelungen ist, die charakteristische negative Steigung, die unser Modell von Anfang an aufwies, zu korrigieren, und dass wir jetzt rentabler werden.

      Unser Kontostand im Laufe der Zeit

      Abb. 24: Die Backtest-Ergebnisse aus der Erprobung unserer neuen modellbasierten Strategie.

      Unser Ziel war es, den durchschnittlichen Gewinn zu erhöhen und den Anteil der Verlustgeschäfte zu verringern, was uns auch gelungen ist. Unser Bruttoverlust betrug im ersten Test 498 $, im zweiten Test 403 $ und jetzt sind es 298 $. Gleichzeitig lag unser Bruttogewinn im ersten Test bei 378 $ und in diesem letzten Test bei 341 $. Die Änderungen, die wir vorgenommen haben, haben also eindeutig zu einer Verringerung des Bruttoverlustes geführt, während der Bruttogewinn nahezu gleich geblieben ist. In unserem ersten System waren 70 % aller unserer Handelsgeschäfte unrentabel. Mit unserem neuen System waren jedoch nur 55 % aller unserer Geschäfte unrentabel.

      Detaillierte Analyse unseres modellbasierten Handelssystems

      Abb. 25: Detaillierte Backtest-Ergebnisse unserer modellbasierten Strategie.

      Schlussfolgerung

      Ausbrüche sind potenziell das beste für den Tageshandel. Die Herausforderung, sie richtig zu identifizieren, ist nicht zu unterschätzen. In diesem Artikel haben wir gemeinsam unsere eigene Ausbruchs-Handelsstrategie entwickelt. Wir haben unserer Strategie weitere Filter hinzugefügt, um sie rentabler zu machen. Es könnte sein, dass Ausbruchsstrategien für den EURUSD-Markt nicht ideal sind und wir uns diesem Markt aus einem anderen Blickwinkel nähern müssen. Der erfolgreiche Aufbau einer Ausbruchs-Handelsstrategie erfordert jedoch mehr Zeit und Mühe, als wir in diesem Artikel dargelegt haben, aber die Ideen, die wir hier dargelegt haben, können auf Ihrem Weg zum Erfolg eine Überlegung wert sein.

      Datei Name 
      Beschreibung
      MQL5 EURUSD AI
      Jupyter-Notebook, mit dem wir unser Modell für den EURUSD-Markt erstellt haben.
      EURUSD MA 60 Model
      ONNX-Modell, das für die Prognose des gleitenden Durchschnitts über 60 Perioden verwendet wird.
      EURUSD MA 60 Height Model ONNX-Modell zur Prognose der Differenz zwischen dem künftigen Schlusskurs und dem künftigen 60-MA.
      EURUSD MA 5 Model
      ONNX-Modell zur Prognose des gleitenden 5-Perioden-Durchschnitts.
      EURUSD MA 5 Height Model ONNX-Modell zur Prognose der Differenz zwischen dem künftigen Schlusskurs und dem künftigen 5-MA 
      MTF Channel 2
      Die erste Umsetzung unserer Ausbruchsstrategie.
      MTF Channel 2 EURUSD
      Die zweite Implementierung unserer Ausbruchsstrategie, die die Bestätigung von Benchmark-Paaren verwendet.
      MTF Channel 2 EURUSD AI
      Die dritte Umsetzung unserer Ausbruchsstrategie, die auf einem Modell basierte.





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

      MQL5 Handels-Toolkit (Teil 4): Entwicklung einer EX5-Bibliothek zur Verwaltung der Handelsgeschichte MQL5 Handels-Toolkit (Teil 4): Entwicklung einer EX5-Bibliothek zur Verwaltung der Handelsgeschichte
      Lernen Sie, wie Sie geschlossene Positionen, Aufträge und Deals mit MQL5 abrufen, verarbeiten, klassifizieren, sortieren, analysieren und verwalten können, indem Sie in einer detaillierten Schritt-für-Schritt-Anleitung eine umfangreiche History Management EX5 Library erstellen.
      Risikomodell für ein Portfolio unter Verwendung des Kelly-Kriteriums und der Monte-Carlo-Simulation Risikomodell für ein Portfolio unter Verwendung des Kelly-Kriteriums und der Monte-Carlo-Simulation
      Seit Jahrzehnten verwenden Händler die Formel des Kelly-Kriteriums, um den optimalen Anteil des Kapitals für eine Investition oder eine Wette zu bestimmen, um das langfristige Wachstum zu maximieren und gleichzeitig das Risiko des Ruins zu minimieren. Das blinde Befolgen des Kelly-Kriteriums auf der Grundlage der Ergebnisse eines einzigen Backtests ist jedoch für einzelne Händler oft gefährlich, da beim Live-Handel der Handelsvorsprung im Laufe der Zeit abnimmt und die vergangene Leistung keine Vorhersage für das zukünftige Ergebnis ist. In diesem Artikel werde ich einen realistischen Ansatz für die Anwendung des Kelly-Kriteriums für die Risikoallokation eines oder mehrerer EAs in MetaTrader 5 vorstellen und dabei die Ergebnisse der Monte-Carlo-Simulation von Python einbeziehen.
      Aufbau des Kerzenmodells Trend Constraint (Teil 9): Expert Advisor für mehrere Strategien (III) Aufbau des Kerzenmodells Trend Constraint (Teil 9): Expert Advisor für mehrere Strategien (III)
      Willkommen zum dritten Teil unserer Trendserie! Heute werden wir uns mit der Verwendung von Divergenzen als Strategie zur Identifizierung optimaler Einstiegspunkte innerhalb des vorherrschenden Tagestrends beschäftigen. Wir werden auch einen nutzerdefinierten Gewinnsicherungsmechanismus einführen, der einem Trailing-Stop-Loss ähnelt, aber einzigartige Verbesserungen aufweist. Darüber hinaus werden wir den Experten Trend Constraint zu einer fortschrittlicheren Version ausbauen und eine neue Handelsausführungsbedingung einführen, die die bestehenden Bedingungen ergänzt. Im weiteren Verlauf werden wir die praktische Anwendung von MQL5 bei der Entwicklung von Algorithmen weiter erforschen und Ihnen tiefer gehende Einblicke und umsetzbare Techniken vermitteln.
      Handel mit dem MQL5 Wirtschaftskalender (Teil 5): Verbessern des Dashboards mit reaktionsschnellen Steuerelementen und Filterschaltflächen Handel mit dem MQL5 Wirtschaftskalender (Teil 5): Verbessern des Dashboards mit reaktionsschnellen Steuerelementen und Filterschaltflächen
      In diesem Artikel erstellen wir Schaltflächen für die Filter von Währungspaar, Wichtigkeitsstufen, Zeitspannen und eine Abbruchoption, um die Kontrolle über das Dashboard zu verbessern. Diese Tasten sind so programmiert, dass sie dynamisch auf Nutzeraktionen reagieren und eine nahtlose Interaktion ermöglichen. Außerdem automatisieren wir ihr Verhalten, um Änderungen in Echtzeit auf dem Dashboard anzuzeigen. Dies verbessert die allgemeine Funktionsweise, Mobilität und Reaktionsfähigkeit des Panels.