Umkehrmuster: Testen des Musters Kopf und Schulter

28 Dezember 2018, 14:05
Dmitriy Gizlyk
0
545

Inhalt

Einführung

Im Artikel "Umkehrmuster: Testen des Musters Doppelspitze/Doppelboden" haben wir die Handelsstrategie auf Basis des Muster von Doppelspitze/Doppelboden besprochen und getestet. Dieser Artikel setzt das Thema der Umkehrmuster mit der grafischen Analyse von Kopf und Schulter fort.


1. Theoretische Aspekte des Musters

Die Muster von Kopf und Schulter und dem umgekehrten Kopf und Schulter gehören zu den bekanntesten und am weitesten verbreiteten grafischen Mustern. Der Name des Musters beschreibt deutlich seine grafische Struktur. Das Muster von Kopf und Schulter bildet sich am Ende eines Aufwärtstrends und bietet Verkaufssignale. Das Muster selbst besteht aus drei aufeinander folgenden Spitzen auf dem Preischart. Die mittlere Spitze überragt die beiden benachbarten, wie ein Kopf die Schultern. Die mittlere Spitze wird als Kopf bezeichnet, die benachbarten Schultern. Die Linie, die die Unterseiten zwischen den Spitzen des Musters verbindet, wird als Nackenlinie bezeichnet. Die Signale der Muster, deren Nackenlinie nach links geneigt ist, gelten als stärker. Das Muster eines umgekehrten Kopf und Schulter ist eine gespiegelte Version von Kopf und Schulter, die eine Aufwärtsbewegung anzeigt.

Das Muster von Kopf und Schulter

Meistens bildet sich das Muster bei einem falschen Ausbruch des Unterstützungs-/Widerstandslinien. Trendfolgende Händler sehen in einer kleinen Korrektur (linke Schulter oben) eine gute Gelegenheit, ihre Position zu erhöhen. Dadurch kehrt der Preis zum aktuellen Trend zurück und bricht das linke Schulterniveau. Nach dem Durchbrechen des aktuellen Unterstützungs-/Widerstandslinien wird der abgeschwächte Trend gestoppt, indem Gegentrendhändler ihre Positionen erhöhen, was zu einer neuen Korrektur führt. Es bildet sich eine Trendumkehr und der Preis fällt wieder unter das Niveau (der Kopf wird gebildet). Ein weiterer Versuch, den Trend wieder aufzunehmen, zeigt seine Schwäche, es entsteht eine kleine Bewegung (die rechte Schulter). An dieser Stelle spüren die Marktteilnehmer eine Trendwende. Trendfolgende Händler verlassen den Markt massenhaft, während gegenläufige Händler ihre Positionen erhöhen. Dies führt zu einer starken Bewegung und der Bildung eines neuen Trends.


2. Strategie das Muster zu handeln

Wie beim Muster von Kopf und Schulter gibt es verschiedene Strategien für den Handel mit Kopf und Schulter. In vielerlei Hinsicht ähneln sie den Handelsstrategien des vorherigen Musters.

2.1. Fall 1

Die erste Strategie basiert auf dem Durchbrechen der Nackenlinie. In diesem Fall wird die Position eröffnet, nachdem die Linie vom Schlusspreis des analysierten Zeitraums durchbrochen wurde. In diesem Fall wird ein Stop-Loss auf dem Musterkopf-Extremniveau oder mit einem kleinen Einzug eingestellt. Der Spreizung des Symbols sollte berücksichtigt werden.

Strategie 1

Der Nachteil dieses Ansatzes ist die Tatsache, dass sich ein Balken bei einer starken Kursbewegung weit entfernt von der Nackenlinie schließen kann. Dies kann zu Gewinnverlusten führen und das Risiko von Verlustposition nach einem bestimmten Muster erhöhen.

Die Variation dieses Falles eröffnet eine Position, nachdem der Preis einen bestimmten festen Abstand von der Nackenlinie in Richtung eines sich abzeichnenden Trends überwunden hat.

2.2. Fall 2

Die zweite Option des Musters — das Öffnen einer Position, wenn der Preis nach dem Durchbrechen wieder zur Nackenlinie zurückkehrt. In diesem Fall wird ein Stop-Loss für das letzte Extremum festgelegt, der es näher heran holt und es einem Händler ermöglicht, eine Position mit dem größeren Volumen bei gleichem Risiko zu eröffnen. Dadurch erhöht sich die potenzielle Gewinn- und Verlustquote.

Fall 2

Wie beim Muster von Doppelspitze/Doppelboden kehrt der Preis nicht immer zur Nackenlinie zurück, nachdem er sie gebrochen hat. Dadurch wird eine beträchtliche Anzahl von Mustern ignoriert. So lassen wir in diesem Fall einige Muster aus und sind meist nicht im Markt engagiert.

Bei beiden Fällen ist der empfohlene kleinste Take-Profit der Abstand zur Nackenlinie, der gleich der Preisbewegung vom Kopf zur Nackenlinie ist.

Take-Profit


3. Erstellen eines Expert Advisor zum Testen der Strategie

Vielleicht haben Sie schon die Ähnlichkeit der Ansätze der Muster von Doppelspitze/Doppelboden und Kopf und Schulter bemerkt. Wir werden den Algorithmus aus dem vorherigen Artikel verwenden, um einen neuen EA zu entwickeln.

Die meisten Vorbereitungsarbeiten im Artikel [1] haben wir bereits durchgeführt. Erstellen wir die Klasse CHS_Pattern, um nach Mustern zu suchen. Sie sollte von der Klasse CPattern abgeleitet werden, damit wir das Entwickelte aus dem vorherigen Artikel anwenden können. Somit haben wir Zugriff auf alle Methoden der übergeordneten Klasse. Hier werden wir nur fehlende Elemente hinzufügen und die Methoden Klasseninitialisierung, Mustersuche und Einstiegspunkt neu schreiben.

class CHS_Pattern : public CPattern
  {
protected:
   s_Extremum     s_HeadExtremum;         //Head extremum point
   s_Extremum     s_StartRShoulder;       //Right shoulder start point

public:
                     CHS_Pattern();
                    ~CHS_Pattern();
//--- Initialize the class
   virtual bool      Create(CTrends *trends, double min_correction, double max_correction);
//--- Pattern and entry point search methods
   virtual bool      Search(datetime start_time);
   virtual bool      CheckSignal(int &signal, double &sl, double &tp1, double &tp2);
//---
   s_Extremum        HeadExtremum(void)      const {  return s_HeadExtremum;     }
   s_Extremum        StartRShoulder(void)    const {  return s_StartRShoulder;   }
  };

In der Methode zur Initialisierung der Klasse initialisieren wir zuerst die Elternklasse und bereiten das Hinzufügen der Strukturen vor.

bool CHS_Pattern::Create(CTrends *trends,double min_correction,double max_correction)
  {
   if(!CPattern::Create(trends,min_correction,max_correction))
      return false;
//---
   s_HeadExtremum.Clear();
   s_StartRShoulder.Clear();
//---
   return true;
  }

In der Methode für das Suchen der Muster prüfen wir, ob die Anzahl der Extrema ausreicht zum Bestimmen der Muster.

bool CHS_Pattern::Search(datetime start_time)
  {
   if(CheckPointer(C_Trends)==POINTER_INVALID || C_Trends.Total()<6)
      return false;

Dann definieren wir den Index des Extremums, der dem angegebenen Zeitpunkt entspricht, zu dem die Suche nach den Mustern beginnen soll. Wenn zum angegebenen Zeitpunkt kein einziges Extremum vorliegt, verlassen wir die Methode mit dem Ergebnis false.

   int start=C_Trends.ExtremumByTime(start_time);
   if(start<0)
      return false;

Als Nächstes laden wir die Daten der letzten sechs Extrema und überprüfen die Preisbewegungen auf Übereinstimmung mit dem entsprechenden Muster. Wenn das Muster gefunden wurde, verlassen wir die Methode mit dem Ergebnis true. Wenn das Muster nicht gefunden wird, verschieben wir es um ein Muster in Richtung des aktuellen Moments und wiederholen die Schleife. Wenn kein Muster nach der Iteration über alle Extrema gefunden wurde, verlassen die Methode mit dem Ergebnis false.

   b_found=false; 
   for(int i=start;i>=0;i--)
     {
      if((i+5)>=C_Trends.Total())
         continue;
      if(!C_Trends.Extremum(s_StartTrend,i+5) || !C_Trends.Extremum(s_StartCorrection,i+4) ||
         !C_Trends.Extremum(s_EndCorrection,i+3)|| !C_Trends.Extremum(s_HeadExtremum,i+2) ||
         !C_Trends.Extremum(s_StartRShoulder,i+1) || !C_Trends.Extremum(s_EndTrend,i))
         continue;
//---
      double trend=MathAbs(s_StartCorrection.Price-s_StartTrend.Price);
      double correction=MathAbs(s_StartCorrection.Price-s_EndCorrection.Price);
      double header=MathAbs(s_HeadExtremum.Price-s_EndCorrection.Price);
      double revers=MathAbs(s_HeadExtremum.Price-s_StartRShoulder.Price);
      double r_shoulder=MathAbs(s_EndTrend.Price-s_StartRShoulder.Price);
      if((correction/trend)<d_MinCorrection || header>(trend-correction)   ||
         (1-fmin(header,revers)/fmax(header,revers))>=d_MaxCorrection      ||
         (1-r_shoulder/revers)<d_MinCorrection || (1-correction/header)<d_MinCorrection)
         continue;
      b_found= true; 
//---
      break;
     }
//---
   return b_found;
  }

Wir schreiben im nächsten Schritt die Suchmethode für Einstiegspunkte neu. Am Anfang der Methode überprüfen wir, ob das Muster zuvor in der analysierten Klasseninstanz gefunden wurde. Wenn das Muster nicht gefunden wird, verlassen wir die Methode mit dem Ergebnis false.

bool CHS_Pattern::CheckSignal(int &signal, double &sl, double &tp1, double &tp2)
  {
   if(!b_found)
      return false;

Dann prüfen wir, wieviele Bars sich nach dem Muster gebildet haben. Wenn sich das Muster gerade gebildet hat, verlassen wir die Methode mit dem Ergebnis false.

   string symbol=C_Trends.Symbol();
   if(symbol=="Not Initilized")
      return false;
   datetime start_time=s_EndTrend.TimeStartBar+PeriodSeconds(C_Trends.Timeframe());
   int shift=iBarShift(symbol,e_ConfirmationTF,start_time);
   if(shift<0)
      return false;

Danach laden wir die benötigte Kurshistorie und bereiten die Hilfsvariablen vor.

   MqlRates rates[];
   int total=CopyRates(symbol,e_ConfirmationTF,0,shift+1,rates);
   if(total<=0)
      return false;
//---
   signal=0;
   sl=tp1=tp2=-1;
   bool up_trend=C_Trends.IsHigh(s_EndTrend);
   int shift1=iBarShift(symbol,e_ConfirmationTF,s_EndCorrection.TimeStartBar,true);
   int shift2=iBarShift(symbol,e_ConfirmationTF,s_StartRShoulder.TimeStartBar,true);
   if(shift1<=0 || shift2<=0)
      return false;
   double koef=(s_StartRShoulder.Price-s_EndCorrection.Price)/(shift1-shift2);
   bool break_neck=false;

Weiter in der Schleife suchen wir nach dem Durchbruch der Nackenlinie mit der anschließenden Preisanpassung. Wir definieren die potenzielle Take-Profits während des Durchbruchs der Nackenlinie. Bei der Suche nach einer Preisrückkehr zur Nackenlinie sollten wir prüfen, ob der Preis ein potenzielles Take-Profit-Level erreicht hat. Wenn der Preis den potentiellen Take-Profit erreicht hat, bevor er zur Nackenlinie zurückgekehrt ist, dann wird das Muster als ungültig angesehen, und wir verlassen die Methode mit dem Ergebnis false. Wenn der Eintrittspunkt bestimmt wurde, definieren wir ein Stop-Loss und verlassen die Methode mit dem Ergebnis true. Beachten Sie, dass der Einstiegspunkt als gültig gilt, wenn er nicht früher als die letzten beiden Balken des analysierten Zeitrahmens gebildet wurde. Andernfalls verlassen wir die Methode mit dem Ergebnis false.

   for(int i=0;i<total;i++)
     {
      if(up_trend)
        {
         if((tp1>0 && rates[i].low<=tp1) || rates[i].high>s_HeadExtremum.Price)
            return false;
         double neck=koef*(shift2-shift-i)+s_StartRShoulder.Price;
         if(!break_neck)
           {
            if(rates[i].close>neck)
               continue;
            break_neck=true;
            tp1=neck-(s_HeadExtremum.Price-neck)*0.9;
            tp2=neck-(neck-s_StartTrend.Price)*0.9;
            tp1=fmax(tp1,tp2);
            continue;
           }
         if(rates[i].high>neck)
           {
            if(sl==-1)
               sl=rates[i].high;
            else
               sl=fmax(sl,rates[i].high);
           }
         if(rates[i].close>neck || sl==-1)
            continue;
         if((total-i)>2)
            return false;
//---
         signal=-1;
         break;
        }
      else
        {
         if((tp1>0 && rates[i].high>=tp1) || rates[i].low<s_HeadExtremum.Price)
            return false;
         double neck=koef*(shift2-shift-i)+s_StartRShoulder.Price;
         if(!break_neck)
           {
            if(rates[i].close<neck)
               continue;
            break_neck=true;
            tp1=neck+(neck-s_HeadExtremum.Price)*0.9;
            tp2=neck+(s_StartTrend.Price-neck)*0.9;
            tp1=fmin(tp1,tp2);
            continue;
           }
         if(rates[i].low<neck)
           {
            if(sl==-1)
               sl=rates[i].low;
            else
               sl=fmin(sl,rates[i].low);
           }
         if(rates[i].close<neck || sl==-1)
            continue;
         if((total-i)>2)
            return false;
//---
         signal=1;
         break;
        }
     }   
//---
   return true;
  }

Der vollständige Code aller Methoden und Funktionen findet sich im Anhang.

Der Code des EAs zum Testen der Strategie wurde vom Artikel [1] fast unverändert übernommen. Die einzige Änderung betrifft den Ersatz der Klasse für die Arbeit mit den Mustern. Der vollständige Code des EAs befindet sich im Anhang.

Der EA wurde mit einen Zeitraum von 10 Monaten im Jahr 2018 getestet. Die Testparameter sind im Bildschirmfoto unten ersichtlich.

Testparameter Testparameter

Die Tests zeigten die Fähigkeit des EA, in dem analysierten Zeitintervall Gewinne zu erzielen. Mehr als 57% der Positionen wurden mit Gewinn abgeschlossen, mit einem Gewinnfaktor von 2,17. Die Anzahl der Positionen ist jedoch wenig inspirierend - nur 14 Positionen innerhalb von 10 Monaten.

Testergebnisse Testergebnisse


4. Kombination der beiden Muster in einem einzigen EA

Als Ergebnis der Arbeit, die in den beiden Artikeln durchgeführt wurde, erhielten wir zwei profitable EAs, die mit grafischen Umkehrmustern arbeiten. Es ist ganz natürlich, dass es zur Steigerung der Gesamteffizienz des Handels sinnvoll wäre, beide Strategien in einem Handelsprogramm zu kombinieren. Wie ich bereits erwähnt habe, ist die von uns entwickelte neue Mustersuchklasse von der vorherigen abgeleitet. Dies vereinfacht unsere Arbeit an der Kombination von Strategien in einem einzigen EA erheblich.

Zuerst identifizieren wir die Klassen. Die Basisklasse CObject enthält die virtuelle Methode Type. Fügen wir sie der Beschreibung unserer Klassen hinzu. In der Klasse CPattern gibt diese Methode den Wert 101 zurück, in der Klasse CHS_Pattern 102. Im Moment verwenden wir nur zwei Muster, so dass wir uns auf numerische Konstanten beschränken können. Bei der Erhöhung der Anzahl der Muster würde ich empfehlen, eine Enumeration zu verwenden, um die Lesbarkeit des Codes zu verbessern.

Anschließend ergänzen wir die Klassenvergleichsmethode mit dem Block für den Vergleich der Klassenarten. Dadurch sieht der Code der Methode wie folgt aus.

int CPattern::Compare(const CPattern *node,const int mode=0) const
  {
   if(Type()>node.Type())
      return -1;
   else
      if(Type()<node.Type())
         return 1;
//---
   if(s_StartTrend.TimeStartBar>node.StartTrend().TimeStartBar)
      return -1;
   else
      if(s_StartTrend.TimeStartBar<node.StartTrend().TimeStartBar)
         return 1;
//---
   if(s_StartCorrection.TimeStartBar>node.StartCorrection().TimeStartBar)
      return -1;
   else
      if(s_StartCorrection.TimeStartBar<node.StartCorrection().TimeStartBar)
         return 1;
//---
   if(s_EndCorrection.TimeStartBar>node.EndCorrection().TimeStartBar)
      return -1;
   else
      if(s_EndCorrection.TimeStartBar<node.EndCorrection().TimeStartBar)
         return 1;
//---
   return 0;
  }

Damit ist die Änderung des Klassencodes abgeschlossen. Nun wollen wir den EA-Code finalisieren. Auch hier werden wir einige Ergänzungen vornehmen. Zuerst entwickeln wir die Funktion, die eine neue Instanz der entsprechenden Klasse in Abhängigkeit von einem erhaltenen Parameter erzeugt. Der Code der Funktion ist recht einfach und besteht nur darin, switch zu verwenden, um die erforderliche Klassenerstellungsmethode aufzurufen.

CPattern *NewClass(int type)
  {
   switch(type)
     {
      case 0:
        return new CPattern();
        break;
      case 1:
        return new CHS_Pattern();
        break;
     }
//---
   return NULL;
  }

Die folgenden Änderungen werden in der Funktion OnTick() vorgenommen. Sie haben nur mit dem neuen Mustersuchblock zu tun. Der Block, der nach Markteintrittspunkten in den bereits gefundenen Mustern sucht, erfordert keine Änderungen durch die Klassenvererbung.

Hier ordnen wir die Schleife so an, dass sie konsequent nach den Mustern eines Typs und dann eines anderen in der Historie sucht. Dazu aktivieren wir den Aufruf der gerade hinzugefügten Funktion in den neuen Erstellungspunkten der Klasseninstanz. Der aktuelle Index des Durchlaufs der Schleife wird in den Parametern an sie übergeben. Der Betriebsalgorithmus des EAs bleibt unverändert.

void OnTick()
  {
//---
.........................
.........................
.........................
//---
   for(int pat=0;pat<2;pat++)
     {
      Pattern=NewClass(pat);
      if(CheckPointer(Pattern)==POINTER_INVALID)
         return;
      if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection))
        {
         delete Pattern;
         continue;
        }
//---
      datetime ss=start_search;
      while(!IsStopped() && Pattern.Search(ss))
        {
         ss=fmax(ss,Pattern.EndTrendTime()+PeriodSeconds(e_TimeFrame));
         bool found=false;
         for(int i=2;i<ar_Objects.Total();i++)
           {
            CPattern *temp=ar_Objects.At(i);
            if(Pattern.Compare(temp,0)==0)
              {
               found=true;
               break;
              }
           }
         if(found)
            continue;
         if(!CheckPattern(Pattern))
            continue;
         if(!ar_Objects.Add(Pattern))
            continue;
         Pattern=NewClass(pat);
         if(CheckPointer(Pattern)==POINTER_INVALID)
            break;
         if(!Pattern.Create(ar_Objects.At(1),d_MinCorrection,d_MaxCorrection))
           {
            delete Pattern;
            break;
           }
        }
      if(CheckPointer(Pattern)!=POINTER_INVALID)
         delete Pattern;
     }
//---
   return;
  }

Damit sind die Änderungen am EA-Code abgeschlossen. Den vollständigen Code finden Sie in der angehängten Datei TwoPatterns.mq5.

Nachdem wir die notwendigen Änderungen vorgenommen haben, werden wir den Test mit den gleichen Parametern durchführen.

Testparameter Testparameter

Die Testergebnisse zeigen, wie sich die Strategien ergänzen. Die EA handelte innerhalb des Testzeitraums 128 Positionen (60% davon waren profitabel). Infolgedessen betrug der EA-Gewinnfaktor 1,94, während der Erholungsfaktor 3,85 betrug. Die vollständigen Testergebnisse werden auf den folgenden Screenshots angezeigt.

TestergebnisseTestergebnisse

5. Testen des Handelssystems

Um die Stabilität des Handelssystems zu testen, haben wir es mit 6 Hauptsymbolen getestet, ohne die Testparameter zu ändern.

Symbol Gewinn Profit Faktor
EURUSD 743.57 1.94
EURJPY 125.13 1.47
GBPJPY
33.93 1.04
EURGBP -191.7
0.82
GBPUSD -371.05 0.60
USDJPY
-657.38 0.31

Wie aus den obigen Tabellenergebnissen ersichtlich, war der EA bei der Hälfte der getesteten Symbole profitabel, während andere Verluste aufwiesen. Diese Tatsache bestätigt einmal mehr, dass das Preisdiagramm jedes Symbols einen individuellen Ansatz erfordert, und vor der Nutzung des EA ist es notwendig, es für bestimmte Bedingungen zu optimieren. Zum Beispiel erlaubte die Optimierung des "Stop Loss Backstep", den Gewinn bei vier Symbolen zu erhöhen und die Verluste bei zweien zu verringern. Dadurch wurde die Gesamtrentabilität des Korbes der Symbole erhöht. Die Ergebnisse werden in der folgenden Tabelle angezeigt.

Symbol Gewinn Profit Faktor Stop Loss Backstep
EURUSD 1020.28 1.78 350
EURJPY 532.54 1.52 400
GBPJPY 208.69 1.17 300
EURGBP 91.45 1.05 450
GBPUSD -315.87 0.55 100
USDJPY -453.08 0.33 100


6. Arrangieren der Handelssignale für verlustbringende Symbole

Gemäß den EA-Testergebnissen im vorherigen Abschnitt gibt es zwei Symbole mit negativen Ergebnissen. Dies sind GBPUSD und USDJPY. Dies kann auf die unzureichende Stärke der betrachteten Muster für die Trendumkehr bei diesen Symbolen hinweisen. Eine stetige Bewegung zur Reduzierung des Saldos führt zur Idee, die Positionen bei einem eingehenden Signal umzukehren.

Was sind also die Zielwerte für eine Umkehrung der Positionen und wo sollten wir einen Stop-Loss setzen? Um diese Fragen zu beantworten, schauen wir, wie das Ergebnis des vorherigen Tests zustande gekommen ist. Der Test zeigte, dass offene Positionen meist durch Stop-Loss geschlossen wurden. Der Preis erreichte den ersten Take-Profit sehr selten. Daher können wir mit der Umkehr einer Position Stop-Loss und Take-Profit 1 tauschen.

Eine detaillierte Untersuchung der Verlustpositionen zeigte, dass Kanäle oft innerhalb der Preisspanne von Fehlersignalen gebildet werden, während die Kanäle in der grafischen Standardanalyse Trendfortschrittsmuster sind. Daher können wir mit einer mit der vorherigen vergleichbaren Preisbewegung rechnen. Unter Bezugnahme auf den Code der Methode CheckSignal (die Klasse der Mustersuche) können wir leicht erkennen, dass der Maßstab der vorherigen Bewegung durch den Take-Profit-2 festgelegt wird. Alles, was wir brauchen, ist, Take-Profit-2 in der gleichen Entfernung vom aktuellen Preis einzustellen, aber in die andere Richtung.

Dies ermöglicht es uns, Positionen umzukehren, indem wir nur den EA-Code ändern, anstatt den Code der Klasse zur Mustersuche zu ändern.

Um eine solche Verhaltensweise zu implementieren, fügen wir dem EA den Parameter ReverseTrade hinzu, der als On/Off-Flag für das Verhalten, Positionen umzukehren, dient.

input bool  ReverseTrade   =  true; //Reverse Deals

Ein einzelner Parameter kann jedoch die Logik der Positionseröffnung nicht ändern. Nehmen wir daher die Änderungen an der Funktion CheckPattern vor. Wir fügen die Variable temp dem Deklarationsblock für lokale Variablen hinzu. Die Variable dient als Zwischenspeicher für den Tausch von Stop-Loss und Take-Profit-1.

bool CheckPattern(CPattern *pattern)
  {
   int signal=0;
   double sl=-1, tp1=-1, tp2=-1;
   if(!pattern.CheckSignal(signal,sl,tp1,tp2))
      return false;
//---
   double price=0;
   double to_close=100;
   double temp=0;
//---

Als Nächstes fügen wir die Prüfung des Flags ReverseTrade der Operation switch hinzu. Wenn das Flag auf false gesetzt ist, verwenden wir die alte Logik. Wenn wir die Positionsumkehr verwenden, tauschen wir Stop-Loss und Take-Profit-1. Als Nächstes berechnen wir Take-Profit in Symbolpunkten neu und übergeben die erhaltenen Werte an die Klasse CLimitTakeProfit. Nach erfolgreichen Durchlaufen aller Iterationen öffnen wir eine Position, die dem empfangenen Signal entgegengesetzt ist.

//---
   switch(signal)
     {
      case 1:
        CLimitTakeProfit::Clear();
        if(!ReverseTrade)
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
           if((tp1-price)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
              if(CLimitTakeProfit::AddTakeProfit((uint)((tp1-price)/_Point),(fabs(tp1-tp2)>=_Point ? 50 : 100)))
                 to_close-=(fabs(tp1-tp2)>=_Point ? 50 : 100);
           if(to_close>0 && (tp2-price)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
              if(!CLimitTakeProfit::AddTakeProfit((uint)((tp2-price)/_Point),to_close))
                 return false;
           if(Trade.Buy(d_Lot,_Symbol,price,sl-i_SL*_Point,0,NULL))
              return false;
          }
        else
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
           temp=tp1;
           tp1=sl-i_SL*_Point;
           sl=temp;
           tp1=(price-tp1)/_Point;
           tp2=(tp2-price)/_Point;
           if(tp1>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(CLimitTakeProfit::AddTakeProfit((uint)(tp1),((tp2-tp1)>=1? 50 : 100)))
                 to_close-=((tp2-tp1)>=1 ? 50 : 100);
           if(to_close>0 && tp2>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(!CLimitTakeProfit::AddTakeProfit((uint)(tp2),to_close))
                 return false;
           if(Trade.Sell(d_Lot,_Symbol,price,sl,0,NULL))
              return false;
          }
        break;
      case -1:
        CLimitTakeProfit::Clear();
        if(!ReverseTrade)
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_BID);
           if((price-tp1)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
              if(CLimitTakeProfit::AddTakeProfit((uint)((price-tp1)/_Point),(fabs(tp1-tp2)>=_Point ? 50 : 100)))
                 to_close-=(fabs(tp1-tp2)>=_Point ? 50 : 100);
           if(to_close>0 && (price-tp2)>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL)*_Point)
              if(!CLimitTakeProfit::AddTakeProfit((uint)((price-tp2)/_Point),to_close))
                 return false;
           if(Trade.Sell(d_Lot,_Symbol,price,sl+i_SL*_Point,0,NULL))
              return false;
          }
        else
          {
           price=SymbolInfoDouble(_Symbol,SYMBOL_ASK);
           temp=tp1;
           tp1=sl+i_SL*_Point;
           sl=temp;
           tp1=(tp1-price)/_Point;
           tp2=(price-tp2)/_Point;
           if(tp1>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(CLimitTakeProfit::AddTakeProfit((uint)(tp1),((tp2-tp1)>=1 ? 50 : 100)))
                 to_close-=((tp2-tp1)>=1 ? 50 : 100);
           if(to_close>0 && tp2>SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL))
              if(!CLimitTakeProfit::AddTakeProfit((uint)(tp2),to_close))
                 return false;
           if(Trade.Buy(d_Lot,_Symbol,price,sl,0,NULL))
              return false;
          }
        break;
     }
//---
   return true;
  }

Damit ist die Verbesserung des EA-Codes abgeschlossen. Der vollständige Code aller Klassen und der Funktion ist im Anhang enthalten.

Testen wir nun die Funktion der Positionsumkehr. Der erste Test ist mit GBPUSD, dem gleichen Zeitintervall und Zeitrahmen durchzuführen. Die EA-Parameter sind auf den folgenden Screenshots zu sehen.

Test der Umkehrung mit GBPUSDTest der Umkehrung mit GBPUSD

Die Testergebnisse zeigten die Effizienz der Lösung. Im getesteten Zeitintervall zeigte der EA (mit aktivierter Umkehrfunktion) einen Gewinn. Von 127 durchgeführten Transaktionen wurden 95 (75,59%) mit Gewinn abgeschlossen. Der Gewinnfaktor betrug 2,35. Die vollständigen Testergebnisse werden auf den folgenden Screenshots angezeigt.

Testergebnisse der Umkehrung mit GBPUSDTestergebnisse der Umkehrung mit GBPUSD

Um die erhaltenen Ergebnisse zu bestätigen, testen wir mit USDJPY Die Testparameter sind im Bildschirmfoto unten ersichtlich.

Test der Umkehrung mit USDJPYTest der Umkehrung mit USDJPY

Der zweite Test der Funktion der Positionsumkehr war auch erfolgreich. Von 108 Positionen wurden 66 (61.11%) mit Gewinn geschlossen, und der Profit-Faktor liegt bei 2.45. Alle Testergebnisse sind in den Bildschirmfotos dargestellt.

Testergebnisse der Umkehrung mit USDJPYTestergebnisse der Umkehrung mit USDJPY

Die Tests zeigen, dass verlustbringende Strategien dank der Umkehrung in profitable gewandelt werden können. Dennoch sollten wir nicht vergessen, dass eine profitable Strategie eines Symbols nicht immer auch bei einem anderen profitabel sein muss.


Schlussfolgerung

Der Artikel hat die Durchführbarkeit von Strategien demonstriert, die auf der Verwendung von standardmäßigen Grafikmustern basieren. Wir haben es geschafft, einen EA zu entwickeln, der über einen langen Zeitraum hinweg Gewinne auf verschiedenen Symbolen generiert. Bei der Verwendung von Handelsstrategien sollten wir jedoch immer die Besonderheiten jedes Symbols berücksichtigen, da eine Strategie unter bestimmten Bedingungen profitabel sein kann und unter anderen Bedingungen zu Verlusten führt. Die Umkehr von Positionen kann die Situation manchmal verbessern, obwohl die Handelsbedingungen berücksichtigt werden sollten.

Dies sind natürlich nur die ersten Schritte zum Aufbau eines profitablen Handelssystems. Das EA kann auf der Grundlage anderer Parameter optimiert werden. Außerdem können seine Funktionen ergänzt werden (Verschiebung eines Stop-Loss' auf einen "Break-Even", Trailing-Stop, etc.). Selbstverständlich würde ich nicht empfehlen, den EA mit Symbolen zu verwenden, die als verlustbringend bekannt sind.

Ich hoffe, meine Erfahrung wird Ihnen helfen, Ihre eigene Handelsstrategie zu entwickeln.

Der im Artikel enthaltene EA dient nur als Demonstration der Strategie. Er sollte verfeinert werden, bevor er auf realen Märkten eingesetzt wird.

Referenzen

  1. Umkehrmuster: Testen des Musters Doppelspitze/Doppelboden
  2. Verwendung von Limit-Orders anstelle von Take-Profit, ohne den ursprünglichen Code des EA zu ändern.

Programme, die im diesem Artikel verwendet werden

# Name Typ Beschreibung
1 ZigZag.mqh Klassenbibliothek Klasse des Indikators ZigZag
2 Trends.mqh Klassenbibliothek Klasse der Trendsuche
3 Pattern.mqh Klassenbibliothek Klasse zur Arbeit mit dem Muster von Doppelspitze/Doppelboden
4 HS_Pattern Klassenbibliothek Klasse für die Arbeit mit dem Muster von Kopf und Schulter
5 LimitTakeProfit.mqh Klassenbibliothek Klasse, um Take-Profits durch Limit-Orders zu ersetzen
6 Header.mqh Bibliothek Header Datei des Eas
7 Head-Shoulders.mq5 Expert Advisor EA auf Basis der Strategie mit Kopf und Schulter
8 TwoPatterns.mq5 Expert Advisor Der EA, der beide Muster kombiniert


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/5358

Beigefügte Dateien |
MQL5.zip (1352.35 KB)
Umkehrmuster: Testen des Musters Doppelspitze/Doppelboden Umkehrmuster: Testen des Musters Doppelspitze/Doppelboden

Händler suchen oft nach Trendwendepunkten, da der Preis das größte Bewegungspotenzial zu Beginn eines neu gebildeten Trends hat. Folglich werden in der technischen Analyse verschiedene Umkehrmuster beschrieben. Doppelspitze/Doppelboden ist einer der bekanntesten und am häufigsten verwendeten. Der Artikel schlägt das Verfahren einer programmgesteuerten Mustererkennung vor. Es wird auch die Rentabilität des Musters anhand von Verlaufsdaten getestet.

Umkehrung: Reduzieren des maximalen Drawdown und Testen anderer Märkte Umkehrung: Reduzieren des maximalen Drawdown und Testen anderer Märkte

In diesem Artikel führen wir die Umkehrtechnik weiter. Wir werden versuchen, den maximalen Saldorückgang auf ein akzeptables Niveau für die zuvor betrachteten Instrumente zu reduzieren. Wir werden sehen, ob die Maßnahmen den Gewinn verringern. Wir werden auch prüfen, wie sich die Umkehrmethode auf anderen Märkten, einschließlich Aktien-, Rohstoff-, Index-, ETF- und Agrarmärkten, bewährt. Achtung, der Artikel enthält viele Bilder!

Umkehrung: Formalisieren des Einstiegspunktes und die Entwicklung eines Algorithmus für den manuellen Handel Umkehrung: Formalisieren des Einstiegspunktes und die Entwicklung eines Algorithmus für den manuellen Handel

Dies ist der letzte Artikel innerhalb der Serie, der sich mit der Strategie des Umkehrhandels beschäftigt. Hier werden wir versuchen, das Problem zu lösen, das die Instabilität der Testergebnisse in früheren Artikeln verursacht hat. Wir werden auch unseren eigenen Algorithmus für den manuellen Handel in jedem Markt mit der Reverse-Strategie entwickeln und testen.

Die eigene, multi-threaded, asynchrone Web-Anfrage in MQL5 Die eigene, multi-threaded, asynchrone Web-Anfrage in MQL5

Der Artikel beschreibt die Bibliothek, mit der Sie die Effizienz von HTTP-Anfragen mit WebRequest in MQL5 erhöhen können. Die Ausführung von WebRequest im nicht-blockierenden Modus verwendet in zusätzliche Threads, die Hilfscharts und Expert Advisors verwendet, um nutzerdefinierte Ereignisse austauschen und gemeinsame Ressourcen lesen. Die Quellcodes sind ebenfalls besprochen und beigefügt.