Der Filter auf basis der History

Andrey Khatimlianskii | 1 März, 2016

Einführung

Es gibt verschiedene Filter: Werte der Indikatoren, Marktvolatilität, Zeit, Wochentag. Sie können alle für die Sortierung der verlustbringenden Trades verwenden. Und es ist ganz einfach, einen solchen Filter im Expert Advisor hinzuzufügen - fügen Sie einfach eine Bedingung vor der Block-Eröffnung.

Aber was sollte getan werden, wenn Sie die EA-History als Filter verwenden möchten? Wenn Sie Ihr Handelssystem nach mehreren erfolglosen Trades ausschalten, werden Sie keine spätere History haben, und deshalb gibt es dann nichts zu analysieren. Zur Lösung dieses Problems müssen wir den Expert Advisor einstellen, damit er virtuell handelt, das heißt, er muss; eine Eröffnung, eine Modifizierung und Schließung der Trades ohne echte Handelsaktivitäten simulieren.

Der Artikel widmet sich diesem Thema.


Experimentelle Strategie

Um unser System zu realisieren, machen wir einige Änderungen im Advisor CrossMACD_DeLuxe.mq4:

Ich werde versuchen, jeden Schritt der EA-Bearbeitung möglichst in Details zu beschreiben. Aber wenn Sie sich dafür nicht interessieren, können Sie einfach den fertigen Expert Advisor herunterladen und zum Abschnitt "Die Sache lohnt nicht" überspringen.


Berechnung der virtuellen Positionen

Also, es erschien ein Signal eine Position zu öffnen. Die Parameter Stoploss und Takeprofit wurden bereits berechnet und alles ist bereit, die Funktion OrderSend() aufzurufen. Genau in diesem Moment öffnen wir ein virtuelles Trade - müssen wir einfach nur alle notwendigen Parameter der Position in entsprechenden Variablen speichern:

void OpenBuy()
  {
    int _GetLastError = 0;
    double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
    _OpenPriceLevel = NormalizeDouble(Ask, Digits);
 
    if(StopLoss > 0)
        _StopLossLevel = NormalizeDouble(_OpenPriceLevel - 
                                     StopLoss*Point, Digits); 
    else
        _StopLossLevel = 0.0; 
    if(TakeProfit > 0)
        _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel + 
                                   TakeProfit*Point, Digits); 
    else
        _TakeProfitLevel = 0.0; 
 
    //---- Eröffnen die virtuelle Position
    virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel, 
                  _TakeProfitLevel);
 
    if(OrderSend(Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3, 
       _StopLossLevel, _TakeProfitLevel, "CrossMACD", 
       _MagicNumber, 0, Green) < 0)
      {
        _GetLastError = GetLastError();
        Alert("Der Fehler OrderSend № ", _GetLastError);
        return(-1);
      }
  }
 
//---- Speichern wir die Parameter der geöffneten Position in  Hauptvariablen
void virtualOrderSend(int type, double openprice, double stoploss,
                      double takeprofit)
  {
    virtTicket = 1;
    virtType = type;
    virtOpenPrice = openprice;
    virtStopLoss = stoploss;
    virtTakeProfit = takeprofit;
  }

Wie Sie sehen können, verwenden wir nur fünf Variable:

int       virtTicket     = 0;   
// Es bestimmt, ob es eine geöffnete virtuelle Position gibt
int       virtType       = 0;   // Der Typ der Position
double    virtOpenPrice  = 0.0; // der Eröffnungskurs der Position
double    virtStopLoss   = 0.0; // Stoploss der Position
double    virtTakeProfit = 0.0; // Takeprofit der Position

Wir brauchen keine anderen Parameter für die Lösung unserer Aufgabe. Wenn Sie alle Funktionen in diesem Beispiel erweitern wollen, fügen Sie einfach die notwendige Anzahl der Variablen hinzu.

Um die Schließung und Modifizierung der Position zu kontrollieren, müssen wir noch etwas tun. Kopieren Sie den Kontrollblock der geöffneten Positionen, den im Expert Advisor gibt, und wechseln Sie die Parameter der realen Orders in virtuellen:

int start()
  {
    // skipped...
 
    //+------------------------------------------------------------------+
    //|Der Kontrollblock der "virtuellen" Positionen                              |
    //+------------------------------------------------------------------+
    if(virtTicket > 0)
      {
        //---- wenn die Buy-Position geöffnet ist,
        if(virtType == OP_BUY)
          {
            //---- wenn MACD die 0-Linie runter gekreuzt hat,
            if(NormalizeDouble(MACD_1 + CloseLuft*Point*0.1, 
               Digits + 1) <= 0.0)
              {
                //---- Schließen wir die Position
                virtOrderClose(Bid);
              }
            //---- wenn das Signal sich nicht ändert, begleiten wir die Position  
            //     von Trailingstop
            else
              {
                if(TrailingStop > 0)
                  {
                    if(NormalizeDouble(Bid - virtOpenPrice, 
                       Digits ) > 0.0)
                      {
                        if(NormalizeDouble( Bid - TrailingStop*Point - 
                           virtStopLoss, Digits) > 0.0 || virtStopLoss < Point)
                        {
                          virtStopLoss = Bid - TrailingStop*Point;
                        }


                      }
                  }
              }
          }
        //---- wenn die SELL-Position geöffnet ist,
        if(virtType == OP_SELL)
          {
            //---- wenn MACD die 0-Linie nach oben gekreuzt hat,
            if(NormalizeDouble(MACD_1 - CloseLuft*Point*0.1, 
               Digits + 1 ) >= 0.0)
              {
                //---- Schließen wir die Position
                virtOrderClose(Ask);
              }
            //---- wenn das Signal sich nicht ändert, begleiten wir die Position  
            //     von Trailingstop
            else
              {
                if ( TrailingStop > 0 )
                  {
                    if(NormalizeDouble( virtOpenPrice - Ask, 
                       Digits ) > 0.0 )
                      {
                        if(NormalizeDouble( virtStopLoss - ( Ask + 
                           TrailingStop*Point ), Digits ) > 0.0 ||
                           virtStopLoss <= Point )
                          {
                            virtStopLoss = Ask + TrailingStop*Point;
                          }
                      }
                  }
              }
          }
      }
    // skipped...
    return(0);
  } 
 
 
//---- Die Schließfunktion der virtuellen Position
void virtOrderClose(double closeprice)
  {
    //---- Speichern wir die Parameter der geschlossenen Position im Array
    ArrayResize(virtClosedOrders, virtClosedOrdersCount + 1);
 
    virtClosedOrders[virtClosedOrdersCount][0] = virtType;
    virtClosedOrders[virtClosedOrdersCount][1] = virtOpenPrice;
    virtClosedOrders[virtClosedOrdersCount][2] = virtStopLoss;
    virtClosedOrders[virtClosedOrdersCount][3] = virtTakeProfit;
    virtClosedOrders[virtClosedOrdersCount][4] = closeprice;
 
    virtClosedOrdersCount ++;
 
    //---- Löschung der Variable
    virtTicket = 0;
    virtType = 0;
    virtOpenPrice = 0.0;
    virtStopLoss = 0.0;
    virtTakeProfit = 0.0;
  }

Sie sehen, dass die Modifizierung einfach einer neuen Wert-Verleihung der Variable virtStopLoss geworden. Und die Schließung ist ziemlich kompliziert - alle Parameter der geschlossenen Order werden im Array gespeichert. Später wird da die gesamte virtuelle History gespeichert. Daraus werden wir die Informationen über die geschlossenen Trades nehmen, um die Entscheidung bezüglich der Eröffnung einer neuen Position zu treffen.

Jetzt müssen wir Schließung der Positionen unter Stoploss und Takeprofit bearbeiten. Zu diesem Zweck fügen wir einige Zeile im erstellten Kontrollblock der virtuellen Positionen hinzu:

if(virtType == OP_BUY)
  {
    //---- Überprüfen, ob SL nicht aktiviert wurde
    if(virtStopLoss > 0.0 && NormalizeDouble(virtStopLoss - Bid, 
       Digits ) >= 0.0)
      {
        virtOrderClose(virtStopLoss);
      }
    //---- Überprüfen, ob TPL nicht aktiviert wurde
    if(virtTakeProfit > 0.0 && NormalizeDouble( Bid - virtTakeProfit, 
       Digits ) >= 0.0)
      {
        virtOrderClose(virtTakeProfit);
      }
  }

Jetzt ist unsere virtuelle History fertig und wir können Filterkriterium hinzufügen.


Was ist "gut" und was ist "schlecht"?

Wir müssen die Positionsöffnung untersagen, nachdem eine bestimmte Bedingung erfüllt wird. Aber welche Bedingung muss man wählen? Mehrere verlustbringenden Trades nacheinander, die Stoploss-Aktivierung oder die Reduzierung des durchschnittlichen Gewinns von mehreren letzten Trades? Es ist schwer, sicher zu beantworten - jede Variante kann ihre Vor- und Nachteile haben.

Zur Überprüfung der Effizienz bei jeder einzelnen Bedingung, lassen Sie uns versuchen, alle drei über die History zu testen:

extern int TradeFiltrVariant = 0;
 
//---- Die Überprüfungsfunktion, ob das reale Handeln  notwendig ist oder nicht 
bool virtCheckCondition()
  {
    int pos, check_pos = 2;
    double last_profit = 0.0, pre_last_profit = 0.0;
    
    //---- Je nach dem Wert des TradeFiltrVariant:
    switch(TradeFiltrVariant)
      {
        //---- 1: Wir verbieten das reale Handeln, wenn zwei letzten Trades verlustbringende waren
        case 1:
          {
            //---- Wenn die virtuelle History genügend Orders enthält,
            if(virtClosedOrdersCount >= check_pos)
              {
                for(pos = 1; pos check_pos; pos ++)
                  {
                    //---- Wenn der Trade profitabel ist, liefern true zurück
                    if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 && 
                        virtClosedOrders[virtClosedOrdersCount-pos][4] - 
                        virtClosedOrders[virtClosedOrdersCount-pos][1] >= 0.0) ||
                        (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 && 
                        virtClosedOrders[virtClosedOrdersCount-pos][1] - 
                        virtClosedOrders[virtClosedOrdersCount-pos][4] >= 0.0))
                      {
                        return(true);
                      }
                    }
              }
            return(false);
          }
        //---- 2: Wir verbieten das reale Handeln, wenn die letzte Position geschlossen wurde 
        //        über Stoploss
        case 2:
          {
            //---- Wenn die virtuelle History genügend Orders enthält,
            if(virtClosedOrdersCount > 0)
              {
                //---- Wenn der Schlusskurs der letzten Order mit Stoploss gleich ist,
                if(virtClosedOrders[virtClosedOrdersCount-1][2] - 
                   virtClosedOrders[virtClosedOrdersCount-1][4] < Point &&
                   virtClosedOrders[virtClosedOrdersCount-1][4] - 
                   virtClosedOrders[virtClosedOrdersCount-1][2] < Point)
                  {
                    return(false);
                  }
              }
            return(true);
          }
        //---- 3: Wir verbieten das reale Handeln, wenn der Gewinn der letzten Position   
        //----    ist geringer als bei der vorletzten Position (oder Verlust höher ist)
        case 3:
          {
            if(virtClosedOrdersCount >= 2)
              {
                if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0)
                  {
                    last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] - 
                                   virtClosedOrders[virtClosedOrdersCount-1][1];
                  }
                else
                  {
                    last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] - 
                                   virtClosedOrders[virtClosedOrdersCount-1][4];
                  }
                if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0)
                  {
                    pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] - 
                                      virtClosedOrders[virtClosedOrdersCount-2][1];
                  }
                else
                  {
                    pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] - 
                                      virtClosedOrders[virtClosedOrdersCount-2][4];
                  }
 
                if(pre_last_profit - last_profit > 0.0)
                  {
                    return(false);
                  }
              }
            return(true);
          }
        //---- Standardmäßig ist der Filter ausgeschaltet, das heißt, die Position wird immer in der Realität geöffnet
        default: return(true);
      }
    return(true);
  }
 
void OpenBuy()
  {
    int _GetLastError = 0;
    double _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel;
    _OpenPriceLevel = NormalizeDouble(Ask, Digits);
 
    if(StopLoss > 0)
      { 
        _StopLossLevel = NormalizeDouble(_OpenPriceLevel - StopLoss*Point, Digits); 
      }
    else
      { 
        _StopLossLevel = 0.0; 
      }
 
    if(TakeProfit > 0)
      { 
        _TakeProfitLevel = NormalizeDouble(_OpenPriceLevel + TakeProfit*Point, Digits); 
      }
    else
      { 
        _TakeProfitLevel = 0.0; 
      }
 
    //---- Eröffnen die virtuelle Position
    virtOrderSend(OP_BUY, _OpenPriceLevel, _StopLossLevel, _TakeProfitLevel);
 
    //---- Wenn der Filter der virtuellen Positionen das Handeln verbietet, verlassen wir es
    if(virtCheckCondition() == false) 
      { 
        return(0); 
      }
 
    if(OrderSend( Symbol(), OP_BUY, 0.1, _OpenPriceLevel, 3, _StopLossLevel, 
          _TakeProfitLevel, "CrossMACD", _MagicNumber, 0, Green) < 0 )
      {
        _GetLastError = GetLastError();
        Alert("Der Fehler OrderSend № ", _GetLastError);
        return(-1);
      }
  }

Wie Sie sehen können, haben wir jetzt eine externe Variable TradeFiltrVariant. Sie ist verantwortlich dafür, welches Filterkriterium ausgewählt wird:

extern int TradeFiltrVariant = 0;
//---- 0: der Filter ist ausgeschaltet, das heißt, die Position wird immer in der Realität geöffnet
//---- 1: Wir verbieten das reale Handeln, wenn zwei letzten Trades verlustbringende waren
//---- 2: Wir verbieten das reale Handeln, wenn die letzte Position über Stopploss geschlossen wurde
//---- 3: Wir verbieten das reale Handeln, wenn der Gewinn der letzten Position, 
//----    ist geringer als bei der vorletzten Position (oder Verlust höher ist)

Testen Sie nun den Expert Advisor mit verschiedenen Filtern und vergleichen Sie die Ergebnisse.


Die Sache lohnt nicht?

Zum Testen habe ich die folgenden Parameter ausgewählt:

Die folgende Tabelle zeigt die Abhängigkeit der Ergebnisse vor dem ausgewählten Filter:

TradeFiltrVariant Der gesamte Gewinn/Verlust Trades insgesamt Gewinn-Trades Verlust-Trades
0
1678.75 41 9 (22%) 32 (78%)
1
105.65 20 2 (10%) 18 (90%)
2
-550.20 11 0 (0%) 11 (100%)
3
1225.13 28 7 (25%) 21 (75%)

Wie Sie sehen können, die Theorie über die Nützlichkeit des Filters noch nicht bestätigt wurde. Darüber hinaus sind die Ergebnisse, die mit dem Filter bekommen wurden, sind schlechter als ohne Filter. Die einzige Ausnahme ist die dritte Variante - die hat den höheren Gewinn-Trades (25% gegen 22%), aber der Gesamtgewinn ist in allen drei Varianten niedriger.

Was haben wir denn falsch gemacht? Wahrscheinlich haben wir falsche Filterkriteriums ausgewählt. Lassen Sie uns versuchen, alle drei Filter in Gegenteil zu ändern, das heißt, das reale Handeln deaktivieren, wenn:

Um den Expert Advisor nicht zu reduzieren, fügen Sie einfach noch drei weitere Werte von TradeFiltrVariant - 4, 5 und 6:

//---- 4: Wir verbieten das reale Handeln, wenn zwei letzten Trades profitabel waren
//---- 5: Wir verbieten das reale Handeln, wenn der letzte Trade profitabel war
//---- 6: das reale Handeln verbieten, wenn der Gewinn der letzten Position, 
//----     höher ist als bei der vorletzten Position (oder Verlust geringer ist)
 
    //---- 4: Wir verbieten das reale Handeln, wenn zwei letzten Trades profitabel waren
    case 4:
      {
        if(virtClosedOrdersCount >= check_pos)
          {
            for(pos = 1; pos check_pos; pos ++)
              {
                //---- Wenn der Trade verlustbringende ist, liefern true zurück
                if((virtClosedOrders[virtClosedOrdersCount-pos][0] == 0 && 
                   virtClosedOrders[virtClosedOrdersCount-pos][1] - 
                   virtClosedOrders[virtClosedOrdersCount-pos][4] > 0.0) ||
                   (virtClosedOrders[virtClosedOrdersCount-pos][0] == 1 && 
                   virtClosedOrders[virtClosedOrdersCount-pos][4] - 
                   virtClosedOrders[virtClosedOrdersCount-pos][1] > 0.0))
                  {
                    return(true);
                  }
              }
          }
        return(false);
      }
    //---- 5: Wir verbieten das reale Handeln, wenn der letzte Trade profitabel war
    case 5:
      {
        if(virtClosedOrdersCount >= 1)
          {
            if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0)
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] - 
                               virtClosedOrders[virtClosedOrdersCount-1][1];
              }
            else
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] - 
                               virtClosedOrders[virtClosedOrdersCount-1][4];
              }
 
            if(last_profit > 0.0)
              {
                return(false);
              }
          }
        return(true);
      }
    //---- 6: das reale Handeln verbieten, wenn der Gewinn der letzten Position, 
    //----     höher ist als bei der vorletzten Position (oder Verlust geringer ist)
    case 6:
      {
        if(virtClosedOrdersCount >= 2)
          {
            if(virtClosedOrders[virtClosedOrdersCount-1][0] == 0) 
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][4] - 
                               virtClosedOrders[virtClosedOrdersCount-1][1];
              }
            else
              {
                last_profit =  virtClosedOrders[virtClosedOrdersCount-1][1] - 
                               virtClosedOrders[virtClosedOrdersCount-1][4];
              }
            if(virtClosedOrders[virtClosedOrdersCount-2][0] == 0)
              {
                pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][4] - 
                                  virtClosedOrders[virtClosedOrdersCount-2][1];
              }
            else
              {
                pre_last_profit = virtClosedOrders[virtClosedOrdersCount-2][1] - 
                                  virtClosedOrders[virtClosedOrdersCount-2][4];
              }
 
            if(last_profit - pre_last_profit > 0.0)
              {
                return(false);
              }
          }
        return(true);
      }

Nun wollen wir drei neue Varianten testen und Sie fügen die in unsere Tabelle hinzu:

AdaptVariantDer gesamte Gewinn/VerlustTrades insgesamtGewinn-TradesVerlust-Trades
0 1678.75
41 9 (22%)
32 (78%)
1 105.65 20 2 (10%)
18 (90%)
2 -550.20 11 0 (0%)
11 (100%)
3 1225.13 28 7 (25%)
21 (75%)
4
1779.24
39 9 (23%) 30 (77%)
5
2178.95
31
9 (29%)
22 (71%)
6
602.32
24
5 (21%)
19 (79%)

Die sechste Variante hat die Hälfte der Trades sortiert - sowohl rentablen als auch verlustbringenden. Die vierte Variante hat zwei verlustbringenden Trades entfernt, der Gesamtgewinn wurde dadurch um 100,49 $ erhöht.

Und die beste war die Variante, die mit dem Verbot des Handelns nach jedem profitablen Trade ist - der Filter hat 10 verlustbringenden Trades entfernt und keinen profitable Trade.

Also, es gibt eine Hoffnung - dass man sogar eine solche einfache und beliebte Strategie verbessern kann!


Schlussfolgerungen

Ich denke, um das System echt zu verbessern, sind solche Filter nicht genug - man muss noch eine viel tiefer Forschung durchführen und neue Schlüsse ziehen.

Die berechneten Filter können daraufhin noch komplizierter sein und auch absolut anders für jede Strategie. Ihre Wirksamkeit hängt direkt von der Abwechslung von profitablen und verlustbringenden Positionen ab.

In diesem Artikel habe ich nur etwas das Thema über Filter betrachtet und hoffe, dass es ein Impuls für die weitere Entwicklung dieses Themas sein wird.