MetaTrader 5 herunterladen

MQL5 Cookbook: Wie man bei der Einrichtung/Änderung von Handelsstufen Fehler vermeidet

27 Juni 2016, 15:41
Anatoli Kazharski
0
318

Einleitung

Als Fortsetzung unserer Arbeit am Expert Advisor aus dem vorangegangenen Beitrag dieser Reihe, mit dem Titel "MQL5 Cookbook: Position-Eigenschaften im MetaTrader 5 Strategietester analysieren", wollen wir diesmal den EA durchviele nützliche Funktionen erweitern und die bereits bestehenden verbessern und optimieren.

In Foren zur MQL-Programmierung tauchen immer wieder Fragen von Anfängern auf, die gerade dabei sind, Handelsstufen einzurichten und zu verändern (Stop Loss, Take Profit und pending Orders). Ich glaube, dass viele unter Ihnen die Protokollmeldung sehr gut kennen, die mit [Ungültige Stops] endet. In diesem Beitrag werden wir Funktionen erzeugen, die die Werte für Handelsstufen normalisieren und auf ihre Exaktheit überprüfen, bevor eine Position eröffnet oder verändert wird.

Diesmal wird der Expert Advisor externe Parameter haben, die im MetaTrader 5 Strategietester optimiert werden können, sodass er auf gewisse Weise einem einfachen Handelssystem gleicht. Bis wir ein echtes Handelssystem entwickeln können, liegt jedoch noch eine Menge Arbeit vor uns. Doch auch Rom entstand nicht an einem Tag, also haben wir noch jede Menge zu tun.

Im weiteren Verlauf des Beitrags wird auch die Optimierung des Codes in den bestehenden Funktionen angesprochen. Auf das Info-Panel wird zu diesem Zeitpunkt nicht eingegangen, da wir uns zuvor noch einige Position-Eigenschaften anschauen müssen , die mittels Standard-Identifikatoren nicht abgerufen werden können (die Verwendung der Abschluss-History ist hier erforderlich). Dennoch wird dieses Thema Gegenstand eines der nächsten Beiträge aus dieser Reihe sein.


Entwicklung des Expert Advisors

Fangen wir also an. Wie immer fügen wir zuerst zusätzliche Aufzählungen, Variablen, Arrays und Hilfsfunktionen am Anfang der Datei ein. Wir brauchen eine Funktion mit der wir leicht die Symboleigenschaften bekommen können. Die gleiche einfache Vorgehensweise müssen wir anwenden, um Position-Eigenschaften zu bekommen.

In den vorangegangenen Beiträgen haben wir gesehen, dass allen Position-Eigenschaften in derGetPositionProperties Funktion sofort globale Variablen zugewiesen wurden. Diesmal versuchen wir eine Möglichkeit einzubauen, um jede Eigenschaft separat zu erhalten. Unten stehen zwei Aufzählungen für die Implementierung des oben genannten. Auf die Funktionen an sich kommen wir etwas später zu sprechen.

//--- Enumeration of position properties
enum ENUM_POSITION_PROPERTIES
  {
   P_SYMBOL        = 0,
   P_MAGIC         = 1,
   P_COMMENT       = 2,
   P_SWAP          = 3,
   P_COMMISSION    = 4,
   P_PRICE_OPEN    = 5,
   P_PRICE_CURRENT = 6,
   P_PROFIT        = 7,
   P_VOLUME        = 8,
   P_SL            = 9,
   P_TP            = 10,
   P_TIME          = 11,
   P_ID            = 12,
   P_TYPE          = 13,
   P_ALL           = 14
  };
//--- Enumeration of symbol properties
enum ENUM_SYMBOL_PROPERTIES
  {
   S_DIGITS       = 0,
   S_SPREAD       = 1,
   S_STOPSLEVEL   = 2,
   S_POINT        = 3,
   S_ASK          = 4,
   S_BID          = 5,
   S_VOLUME_MIN   = 6,
   S_VOLUME_MAX   = 7,
   S_VOLUME_LIMIT = 8,
   S_VOLUME_STEP  = 9,
   S_FILTER       = 10,
   S_UP_LEVEL     = 11,
   S_DOWN_LEVEL   = 12,
   S_ALL          = 13
  };

Die ENUM_SYMBOL_PROPERTIES Aufzählung enthält nicht alle Symboleigenschaften, doch können diese ggf. jederzeit hinzugefügt werden. Die Aufzählung enthält auch benutzerdefinierte Eigenschaften (10, 11, 12), der Berechnung auf den anderen Symboleigenschaften beruht. Es gibt einen Identifikator mit dem man sofort alle Eigenschaften aus der Aufzählung bekommen kann, wie in der Aufzählung der Position-Eigenschaften.

Diesem folgen die externen Parameter des Expert Advisors:

//--- External parameters of the Expert Advisor
input int            NumberOfBars=2;     // Number of Bullish/Bearish bars for a Buy/Sell
input double         Lot         =0.1;   // Lot
input double         StopLoss    =50;    // Stop Loss
input double         TakeProfit  =100;   // Take Profit
input double         TrailingStop=10;    // Trailing Stop
input bool           Reverse     =true;  // Position reverse

Sehen wir uns die externen Parameter mal genauer an:

  • NumberOfBars - dieser Parameter richtet die Zahl der Bars in einer Richtung für die Eröffnung einer Position ein;
  • Lot - Position-Volumen;
  • TakeProfit - Take Profit-Stufe in Punkten. Ein Null-Wert heißt, dass kein Take Profit eingerichtet werden muss.
  • StopLoss - Stop Loss-Stufe in Punkten. Ein Null-Wert heißt, dass kein Stop Loss eingerichtet werden muss.
  • TrailingStop - Trailing Stop Wert in Punkten. Bei einer KAUFEN-Position beruht die Berechnung auf dem Minimum des Bars (Minimum minus der Anzahl der Punkte aus dem StopLoss-Parameter). Bei einer VERKAUFEN-Position beruht die Berechnung auf dem Maximum des Bars (Maximum plus der Anzahl der Punkte aus dem StopLoss-Parameter) Ein Null-Wert heißt, dass der Trailing Stop nicht aktiviert ist.
  • Reverse - aktiviert/deaktiviert die Umkehr-Position.

Als einziger muss der NumberOfBars Parameter noch ausführlicher erklärt werden. Den Wert dieses Parameters auf, sagen wir, mehr als 5 zu setzen, macht keinen Sinn, da dies ziemlich selten ist und es nach so einer Bewegung für die Eröffnung einer Position sowieso bereits zu spät wäre. Wir brauchen daher eine Variable, mit der wir den Wert dieses Parameters anpassen können:

//--- To check the value of the NumberOfBars external parameter
int                  AllowedNumberOfBars=0;

Dieser Parameter legt zudem auch die Menge der Bardaten fest, die in den Kurs-Arrays abgelegt werden. Wir kommen hierauf gleich zu sprechen, dann nämlich, wenn wir die angepassten Funktionen verändern.

Wie bei den Position-Eigenschaften auch, deklarieren wir Variablen in globalem Umfang für Symboleigenschaften, damit von jeder Funktion auf sie zugegriffen werden kann:

//--- Symbol properties
int                  sym_digits=0;           // Number of decimal places
int                  sym_spread=0;           // Spread in points
int                  sym_stops_level=0;      // Stops level
double               sym_point=0.0;          // Point value
double               sym_ask=0.0;            // Ask price
double               sym_bid=0.0;            // Bid price
double               sym_volume_min=0.0;     // Minimum volume for a deal
double               sym_volume_max=0.0;     // Maximum volume for a deal
double               sym_volume_limit=0.0;   // Maximum permissible volume for a position and orders in one direction
double               sym_volume_step=0.0;    // Minimum volume change step for a deal
double               sym_offset=0.0;         // Offset from the maximum possible price for a transaction
double               sym_up_level=0.0;       // Upper Stop level price
double               sym_down_level=0.0;     // Lower Stop level price

Da der Trailing Stop auf Basis des Hoch und Niedrig des Bars berechnet werden muss, brauchen wir solche Bar-Daten auch Arrays:

//--- Price data arrays
double               close_price[]; // Close (closing prices of the bar)
double               open_price[];  // Open (opening prices of the bar)
double               high_price[];  // High (bar's highs)
double               low_price[];   // Low (bar's lows)

Wenden wir uns nun der Veränderung und Erzeugung von Funktionen zu. Wir haben ja bereits die GetBarsData Funktion, die die Eröffnungs- und Schlusskurse der Bars in die Kurs-Arrays kopiert. Jetzt brauchen wir zusätzlich noch Hoch und Niedrig. Zusätzlich dazu sollte auch der von dem NumberOfBars Parameter erhaltene Wert angepasst werden. Und so sieht die Funktion nach ihrer Veränderung aus:

//+------------------------------------------------------------------+
//| Getting bar values                                               |
//+------------------------------------------------------------------+
void GetBarsData()
  {
//--- Adjust the number of bars for the position opening condition
   if(NumberOfBars<=1)
      AllowedNumberOfBars=2;              // At least two bars are required
   if(NumberOfBars>=5)
      AllowedNumberOfBars=5;              // but no more than 5
   else
      AllowedNumberOfBars=NumberOfBars+1; // and always more by one
//--- Reverse the indexing order (... 3 2 1 0)
   ArraySetAsSeries(close_price,true);
   ArraySetAsSeries(open_price,true);
   ArraySetAsSeries(high_price,true);
   ArraySetAsSeries(low_price,true);
//--- Get the closing price of the bar
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyClose(_Symbol,Period(),0,AllowedNumberOfBars,close_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Close price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the opening price of the bar
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyOpen(_Symbol,Period(),0,AllowedNumberOfBars,open_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Open price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the bar's high
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyHigh(_Symbol,Period(),0,AllowedNumberOfBars,high_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the High price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
//--- Get the bar's low
//    If the number of the obtained values is less than requested, print the relevant message
   if(CopyLow(_Symbol,Period(),0,AllowedNumberOfBars,low_price)<AllowedNumberOfBars)
     {
      Print("Failed to copy the values ("
            +_Symbol+", "+TimeframeToString(Period())+") to the Low price array! "
            "Error "+IntegerToString(GetLastError())+": "+ErrorDescription(GetLastError()));
     }
  }

Die Bedingungen, die mind. zwei Bars und immer einen mehr verlangen, sind deshalb vorhanden, weil wir nur mir abgeschlossenen Bars, die mit dem Index [1] beginnen, arbeiten. In der Tat können Anpassungen in diesem Fall als hinfällig angesehen werden, da die Bar-Daten, angefangen bei dem im dritten Parameter der CopyOpen, CopyClose, CopyHigh und CopyLow Funktionen, angegebenen Index kopiert werden können. Die Grenze von 5 Bars kann ebenfalls verändert werden (nach oben oder nach unten) - ganz nach Ihrem eigenen Ermessen.

Die GetTradingSignal Funktion ist jetzt etwas komplexer geworden, da die Bedingung unterschiedlich generiert wird, je nach Anzahl der im NumberOfBars Parameter angegebenen Bars. Des Weiteren verwenden wir jetzt eine korrektere Art des gelieferten Wertes - Order-Typ:

//+------------------------------------------------------------------+
//| Determining trading signals                                      |
//+------------------------------------------------------------------+
ENUM_ORDER_TYPE GetTradingSignal()
  {
//--- A Buy signal (ORDER_TYPE_BUY) :
   if(AllowedNumberOfBars==2 && 
      close_price[1]>open_price[1])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars==3 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars==4 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2] && 
      close_price[3]>open_price[3])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars==5 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2] && 
      close_price[3]>open_price[3] && 
      close_price[4]>open_price[4])
      return(ORDER_TYPE_BUY);
   if(AllowedNumberOfBars>=6 && 
      close_price[1]>open_price[1] && 
      close_price[2]>open_price[2] && 
      close_price[3]>open_price[3] && 
      close_price[4]>open_price[4] && 
      close_price[5]>open_price[5])
      return(ORDER_TYPE_BUY);
//--- A Sell signal (ORDER_TYPE_SELL) :
   if(AllowedNumberOfBars==2 && 
      close_price[1]<open_price[1])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars==3 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars==4 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2] && 
      close_price[3]<open_price[3])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars==5 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2] && 
      close_price[3]<open_price[3] && 
      close_price[4]<open_price[4])
      return(ORDER_TYPE_SELL);
   if(AllowedNumberOfBars>=6 && 
      close_price[1]<open_price[1] && 
      close_price[2]<open_price[2] && 
      close_price[3]<open_price[3] && 
      close_price[4]<open_price[4] && 
      close_price[5]<open_price[5])
      return(ORDER_TYPE_SELL);
//--- No signal (WRONG_VALUE):
   return(WRONG_VALUE);
  }

Verändern wir nun die GetPositionProperties Funktion. Mit ihrer Hilfe haben wir in den vorangegangenen Beiträgen alle Eigenschaften auf einmal bekommen. Doch manchmal möchte man eben nur eine Eigenschaften haben. Und dazu können Sie zweifellos die, von der Sprache angebotenen Standardfunktionen verwenden, doch das wäre nicht so bequem wie wir es haben möchten. Unten steht der Code der veränderten GetPositionProperties Funktion. Wenn wir jetzt einen bestimmten Identifikator von der ENUM_POSITION_PROPERTIES Aufzählung übertragen, erhält man entweder eine bestimmte einzelne Position-Eigenschaft oder alle Eigenschaften auf einmal.

//+------------------------------------------------------------------+
//| Getting position properties                                      |
//+------------------------------------------------------------------+
void GetPositionProperties(ENUM_POSITION_PROPERTIES position_property)
  {
//--- Check if there is an open position
   pos_open=PositionSelect(_Symbol);
//--- If an open position exists, get its properties
   if(pos_open)
     {
      switch(position_property)
        {
         case P_SYMBOL        : pos_symbol=PositionGetString(POSITION_SYMBOL);                  break;
         case P_MAGIC         : pos_magic=PositionGetInteger(POSITION_MAGIC);                   break;
         case P_COMMENT       : pos_comment=PositionGetString(POSITION_COMMENT);                break;
         case P_SWAP          : pos_swap=PositionGetDouble(POSITION_SWAP);                      break;
         case P_COMMISSION    : pos_commission=PositionGetDouble(POSITION_COMMISSION);          break;
         case P_PRICE_OPEN    : pos_price=PositionGetDouble(POSITION_PRICE_OPEN);               break;
         case P_PRICE_CURRENT : pos_cprice=PositionGetDouble(POSITION_PRICE_CURRENT);           break;
         case P_PROFIT        : pos_profit=PositionGetDouble(POSITION_PROFIT);                  break;
         case P_VOLUME        : pos_volume=PositionGetDouble(POSITION_VOLUME);                  break;
         case P_SL            : pos_sl=PositionGetDouble(POSITION_SL);                          break;
         case P_TP            : pos_tp=PositionGetDouble(POSITION_TP);                          break;
         case P_TIME          : pos_time=(datetime)PositionGetInteger(POSITION_TIME);           break;
         case P_ID            : pos_id=PositionGetInteger(POSITION_IDENTIFIER);                 break;
         case P_TYPE          : pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); break;
         case P_ALL           :
            pos_symbol=PositionGetString(POSITION_SYMBOL);
            pos_magic=PositionGetInteger(POSITION_MAGIC);
            pos_comment=PositionGetString(POSITION_COMMENT);
            pos_swap=PositionGetDouble(POSITION_SWAP);
            pos_commission=PositionGetDouble(POSITION_COMMISSION);
            pos_price=PositionGetDouble(POSITION_PRICE_OPEN);
            pos_cprice=PositionGetDouble(POSITION_PRICE_CURRENT);
            pos_profit=PositionGetDouble(POSITION_PROFIT);
            pos_volume=PositionGetDouble(POSITION_VOLUME);
            pos_sl=PositionGetDouble(POSITION_SL);
            pos_tp=PositionGetDouble(POSITION_TP);
            pos_time=(datetime)PositionGetInteger(POSITION_TIME);
            pos_id=PositionGetInteger(POSITION_IDENTIFIER);
            pos_type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);                     break;
         default: Print("The passed position property is not listed in the enumeration!");               return;
        }
     }
//--- If there is no open position, zero out variables for position properties
   else
      ZeroPositionProperties();
  }

Analog implementieren wir die GetSymbolProperties Funktion, um n die Symboleigenschaften zu kommen:

//+------------------------------------------------------------------+
//| Getting symbol properties                                        |
//+------------------------------------------------------------------+
void GetSymbolProperties(ENUM_SYMBOL_PROPERTIES symbol_property)
  {
   int lot_offset=1; // Number of points for the offset from the Stops level
//---
   switch(symbol_property)
     {
      case S_DIGITS        : sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);                   break;
      case S_SPREAD        : sym_spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);                   break;
      case S_STOPSLEVEL    : sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);   break;
      case S_POINT         : sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);                           break;
      //---
      case S_ASK           :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),sym_digits);                       break;
      case S_BID           :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),sym_digits);                       break;
         //---
      case S_VOLUME_MIN    : sym_volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);                 break;
      case S_VOLUME_MAX    : sym_volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);                 break;
      case S_VOLUME_LIMIT  : sym_volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);             break;
      case S_VOLUME_STEP   : sym_volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);               break;
      //---
      case S_FILTER        :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*sym_point),sym_digits);        break;
         //---
      case S_UP_LEVEL      :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),sym_digits);
         sym_up_level=NormalizeDouble(sym_ask+sym_stops_level*sym_point,sym_digits);                     break;
         //---
      case S_DOWN_LEVEL    :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),sym_digits);
         sym_down_level=NormalizeDouble(sym_bid-sym_stops_level*sym_point,sym_digits);                   break;
         //---
      case S_ALL           :
         sym_digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS);
         sym_spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD);
         sym_stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
         sym_point=SymbolInfoDouble(_Symbol,SYMBOL_POINT);
         sym_ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),sym_digits);
         sym_bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),sym_digits);
         sym_volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
         sym_volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
         sym_volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT);
         sym_volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
         sym_offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*sym_point),sym_digits);
         sym_up_level=NormalizeDouble(sym_ask+sym_stops_level*sym_point,sym_digits);
         sym_down_level=NormalizeDouble(sym_bid-sym_stops_level*sym_point,sym_digits);                   break;
         //---
      default: Print("The passed symbol property is not listed in the enumeration!"); return;
     }
  }

Bitte beachten Sie, dass Sie für einige Symboleigenschaften zuerst andere Eigenschaften bekommen müssen.

Wir haben eine neue Funktion: CorrectValueBySymbolDigits. Sie liefert den relevanten Wert, je nach Anzahl der Dezimalstellen im Kurs. An diese Funktion kann eine ganze oder echte Zahl übertragen werden. Die Art der übertragenen Daten legt die Version der Funktion fest, die verwendet werden soll. Dieses Mermal nennt man Funktionsüberladung.

//+------------------------------------------------------------------+
//| Adjusting the value based on the number of digits in the price (int)|
//+------------------------------------------------------------------+
int CorrectValueBySymbolDigits(int value)
  {
   return (sym_digits==3 || sym_digits==5) ? value*=10 : value;
  }
//+------------------------------------------------------------------+
//| Adjusting the value based on the number of digits in the price (double)|
//+------------------------------------------------------------------+
double CorrectValueBySymbolDigits(double value)
  {
   return (sym_digits==3 || sym_digits==5) ? value*=10 : value;
  }

Unser Expert Advisor wird einen externen Parameter zur Spezifizierung des Volumens (Posten) der Eröffnungs-Position haben. Dazu erzeugen wir eine Funktion, die den Posten in Übereinstimmung mit der Symbol-Spezifikation anpasst - CalculateLot:

//+------------------------------------------------------------------+
//| Calculating position lot                                         |
//+------------------------------------------------------------------+
double CalculateLot(double lot)
  {
//--- To adjust as per the step
   double corrected_lot=0.0;
//---
   GetSymbolProperties(S_VOLUME_MIN);  // Get the minimum possible lot
   GetSymbolProperties(S_VOLUME_MAX);  // Get the maximum possible lot
   GetSymbolProperties(S_VOLUME_STEP); // Get the lot increase/decrease step
//--- Adjust as per the lot step
   corrected_lot=MathRound(lot/sym_volume_step)*sym_volume_step;
//--- If less than the minimum, return the minimum
   if(corrected_lot<sym_volume_min)
      return(NormalizeDouble(sym_volume_min,2));
//--- If greater than the maximum, return the maximum
   if(corrected_lot>sym_volume_max)
      return(NormalizeDouble(sym_volume_max,2));
//---
   return(NormalizeDouble(corrected_lot,2));
  }

Wenden wir uns nun den Funktionen zu, die direkt mit dem Titel dieses Beitrags zu tun haben. Sie sind ziemlich einfach und eindeutig, sodass Sie ihren Zweck mit Hilfe der Anmerkungen im Code ohne Mühe verstehen werden.

Die Funktion CalculateTakeProfit dient zur Berechnung des Take Profit Werts:

//+------------------------------------------------------------------+
//| Calculating the Take Profit value                                |
//+------------------------------------------------------------------+
double CalculateTakeProfit(ENUM_ORDER_TYPE order_type)
  {
//--- If Take Profit is required
   if(TakeProfit>0)
     {
      //--- For the calculated Take Profit value
      double tp=0.0;
      //--- If you need to calculate the value for a SELL position
      if(order_type==ORDER_TYPE_SELL)
        {
         //--- Calculate the level
         tp=NormalizeDouble(sym_bid-CorrectValueBySymbolDigits(TakeProfit*sym_point),sym_digits);
         //--- Return the calculated value if it is lower than the lower limit of the Stops level
         //    If the value is higher or equal, return the adjusted value
         return(tp<sym_down_level ? tp : sym_down_level-sym_offset);
        }
      //--- If you need to calculate the value for a BUY position
      if(order_type==ORDER_TYPE_BUY)
        {
         //--- Calculate the level
         tp=NormalizeDouble(sym_ask+CorrectValueBySymbolDigits(TakeProfit*sym_point),sym_digits);
         //--- Return the calculated value if it is higher that the upper limit of the Stops level
         //    If the value is lower or equal, return the adjusted value
         return(tp>sym_up_level ? tp : sym_up_level+sym_offset);
        }
     }
//---
   return(0.0);
  }

Die Funktion CalculateStopLoss dient zur Berechnung des Stop Loss Werts:

//+------------------------------------------------------------------+
//| Calculating the Stop Loss value                                  |
//+------------------------------------------------------------------+
double CalculateStopLoss(ENUM_ORDER_TYPE order_type)
  {
//--- If Stop Loss is required
   if(StopLoss>0)
     {
      //--- For the calculated Stop Loss value
      double sl=0.0;
      //--- If you need to calculate the value for a BUY position
      if(order_type==ORDER_TYPE_BUY)
        {
         // Calculate the level
         sl=NormalizeDouble(sym_ask-CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- Return the calculated value if it is lower that the lower limit of the Stops level
         //    If the value is higher or equal, return the adjusted value
         return(sl<sym_down_level ? sl : sym_down_level-sym_offset);
        }
      //--- If you need to calculate the value for a SELL position
      if(order_type==ORDER_TYPE_SELL)
        {
         //--- Calculate the level
         sl=NormalizeDouble(sym_bid+CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- Return the calculated value if it is higher than the upper limit of the Stops level
         //    If the value is lower or equal, return the adjusted value
         return(sl>sym_up_level ? sl : sym_up_level+sym_offset);
        }
     }
//---
   return(0.0);
  }

Die Funktion CalculateTrailingStop dient zur Berechnung des Trailing Stop Werts:

//+------------------------------------------------------------------+
//| Calculating the Trailing Stop value                              |
//+------------------------------------------------------------------+
double CalculateTrailingStop(ENUM_POSITION_TYPE position_type)
  {
//--- Variables for calculations
   double            level       =0.0;
   double            buy_point   =low_price[1];    // The Low value for a Buy
   double            sell_point  =high_price[1];   // The High value for a Sell
//--- Calculate the level for a BUY position
   if(position_type==POSITION_TYPE_BUY)
     {
      //--- Bar's low minus the specified number of points
      level=NormalizeDouble(buy_point-CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
      //--- If the calculated level is lower than the lower limit of the Stops level, 
      //    the calculation is complete, return the current value of the level
      if(level<sym_down_level)
         return(level);
      //--- If it is not lower, try to calculate based on the bid price
      else
        {
         level=NormalizeDouble(sym_bid-CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- If the calculated level is lower than the limit, return the current value of the level
         //    otherwise set the nearest possible value
         return(level<sym_down_level ? level : sym_down_level-sym_offset);
        }
     }
//--- Calculate the level for a SELL position
   if(position_type==POSITION_TYPE_SELL)
     {
      // Bar's high plus the specified number of points
      level=NormalizeDouble(sell_point+CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
      //--- If the calculated level is higher than the upper limit of the Stops level, 
      //    the calculation is complete, return the current value of the level
      if(level>sym_up_level)
         return(level);
      //--- If it is not higher, try to calculate based on the ask price
      else
        {
         level=NormalizeDouble(sym_ask+CorrectValueBySymbolDigits(StopLoss*sym_point),sym_digits);
         //--- If the calculated level is higher than the limit, return the current value of the level
         //    Otherwise set the nearest possible value
         return(level>sym_up_level ? level : sym_up_level+sym_offset);
        }
     }
//---
   return(0.0);
  }

Jetzt haben wir also alle notwendigen Funktionen zur Lieferung korrekter Werte für Handelsoperationen. Erzeugen wir nun eine Funktion, die die Bedingung für die Veränderung des Trailing Stops prüft und ihn verändert, wenn diese angegebene Bedingung erfüllt ist: ModifyTrailingStop. Unten steht der Code mit detaillierten Anmerkungen.

Bitte achten Sie auf die Verwendung aller oben erzeugten/veränderten Funktionen. Der Wechsel "Schalter" legt die relevante Bedingung je nach Art der aktuellen Position fest und das Ergebnis der Bedingung wird dann in der condition Variable abgelegt. Zur Veränderung einer Position verwenden wir die PositionModify Methode aus der CTrade Klasse der Standard-Library.

//+------------------------------------------------------------------+
//| Modifying the Trailing Stop level                                |
//+------------------------------------------------------------------+
void ModifyTrailingStop()
  {
//--- If the Trailing Stop and Stop Loss are set
   if(TrailingStop>0 && StopLoss>0)
     {
      double         new_sl=0.0;       // For calculating the new Stop Loss level
      bool           condition=false;  // For checking the modification condition
      //--- Get the flag of presence/absence of the position
      pos_open=PositionSelect(_Symbol);
      //--- If the position exists
      if(pos_open)
        {
         //--- Get the symbol properties
         GetSymbolProperties(S_ALL);
         //--- Get the position properties
         GetPositionProperties(P_ALL);
         //--- Get the Stop Loss level
         new_sl=CalculateTrailingStop(pos_type);
         //--- Depending on the position type, check the relevant condition for the Trailing Stop modification
         switch(pos_type)
           {
            case POSITION_TYPE_BUY  :
               //--- If the new Stop Loss value is higher
               //    than the current value plus the set step
               condition=new_sl>pos_sl+CorrectValueBySymbolDigits(TrailingStop*sym_point);
               break;
            case POSITION_TYPE_SELL :
               //--- If the new Stop Loss value is lower
               //    than the current value minus the set step
               condition=new_sl<pos_sl-CorrectValueBySymbolDigits(TrailingStop*sym_point);
               break;
           }
         //--- If there is a Stop Loss, compare the values before modification
         if(pos_sl>0)
           {
            //--- If the condition for the order modification is met, i.e. the new value is lower/higher 
            //    than the current one, modify the Trailing Stop of the position
            if(condition)
              {
               if(!trade.PositionModify(_Symbol,new_sl,pos_tp))
                  Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
              }
           }
         //--- If there is no Stop Loss, simply set it
         if(pos_sl==0)
           {
            if(!trade.PositionModify(_Symbol,new_sl,pos_tp))
               Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
     }
  }

Passen wir nun noch die Funktion TradingBlock an, damit sie mit allen oben gemachten Veränderungen übereinstimmt. Wie bei der ModifyTrailingStop Funktion, werden alle Werte der Variablen für eine Handels-Order mit Hilfe des Wechsel "Schalters" festgelegt. Sie verringert die Menge an Code ganz erheblich und vereinfacht weitere Veränderungen, da nun, anstatt eines Zweigs für zwei Position-Arten nur noch einer übrig bleibt.

//+------------------------------------------------------------------+
//| Trading block                                                    |
//+------------------------------------------------------------------+
void TradingBlock()
  {
   ENUM_ORDER_TYPE      signal=WRONG_VALUE;                 // Variable for getting a signal
   string               comment="hello :)";                 // Position comment
   double               tp=0.0;                             // Take Profit
   double               sl=0.0;                             // Stop Loss
   double               lot=0.0;                            // Volume for position calculation in case of reverse position
   double               position_open_price=0.0;            // Position opening price
   ENUM_ORDER_TYPE      order_type=WRONG_VALUE;             // Order type for opening a position
   ENUM_POSITION_TYPE   opposite_position_type=WRONG_VALUE; // Opposite position type
//--- Get a signal
   signal=GetTradingSignal();
//--- If there is no signal, exit
   if(signal==WRONG_VALUE)
      return;
//--- Find out if there is a position
   pos_open=PositionSelect(_Symbol);
//--- Get all symbol properties
   GetSymbolProperties(S_ALL);
//--- Determine values for trade variables
   switch(signal)
     {
      //--- Assign values to variables for a BUY
      case ORDER_TYPE_BUY  :
         position_open_price=sym_ask;
         order_type=ORDER_TYPE_BUY;
         opposite_position_type=POSITION_TYPE_SELL;
         break;
         //--- Assign values to variables for a SELL
      case ORDER_TYPE_SELL :
         position_open_price=sym_bid;
         order_type=ORDER_TYPE_SELL;
         opposite_position_type=POSITION_TYPE_BUY;
         break;
     }
//--- Calculate the Take Profit and Stop Loss levels
   sl=CalculateStopLoss(order_type);
   tp=CalculateTakeProfit(order_type);
//--- If there is no position
   if(!pos_open)
     {
      //--- Adjust the volume
      lot=CalculateLot(Lot);
      //--- Open a position
      //    If the position failed to open, print the relevant message
      if(!trade.PositionOpen(_Symbol,order_type,lot,position_open_price,sl,tp,comment))
        {
         Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
        }
     }
//--- If there is a position
   else
     {
      //--- Get the position type
      GetPositionProperties(P_TYPE);
      //--- If the position is opposite to the signal and the position reverse is enabled
      if(pos_type==opposite_position_type && Reverse)
        {
         //--- Get the position volume
         GetPositionProperties(P_VOLUME);
         //--- Adjust the volume
         lot=pos_volume+CalculateLot(Lot);
         //--- Open the position. If the position failed to open, print the relevant message
         if(!trade.PositionOpen(_Symbol,order_type,lot,position_open_price,sl,tp,comment))
           {
            Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
           }
        }
     }
//---
   return;
  }

In der SetInfoPanel Funktion müssen wir auch noch eine wichtige Korrektur vornehmen, doch zuvor wollen wir erst einige Hilfsfunktionen vorbereiten, die angeben, wie und wo das Programm derzeit verwendet wird:

//+------------------------------------------------------------------+
//| Returning the testing flag                                       |
//+------------------------------------------------------------------+
bool IsTester()
  {
   return(MQL5InfoInteger(MQL5_TESTER));
  }
//+------------------------------------------------------------------+
//| Returning the optimization flag                                  |
//+------------------------------------------------------------------+
bool IsOptimization()
  {
   return(MQL5InfoInteger(MQL5_OPTIMIZATION));
  }
//+------------------------------------------------------------------+
//| Returning the visual testing mode flag                           |
//+------------------------------------------------------------------+
bool IsVisualMode()
  {
   return(MQL5InfoInteger(MQL5_VISUAL_MODE));
  }
//+------------------------------------------------------------------+
//| Returning the flag for real time mode outside the Strategy Tester|
//| if all conditions are met                                        |
//+------------------------------------------------------------------+
bool IsRealtime()
  {
   if(!IsTester() && !IsOptimization() && !IsVisualMode())
      return(true);
   else
      return(false); 
  }

Das einzige was wir der SetInfoPanel Funktion hinzufügen müssen, ist eine Bedingung, die dem Programm sagt, dass das Info-Panel nur im Visualisierungs- und Echtzeit-Modus angezeigt werden soll. Wird dies übersehen, dauert die Zeit für das Testen 4-5 Mal länger Dies ist insbesondere bei der Optimierung der Parameter wichtig.

//+------------------------------------------------------------------+
//| Setting the info panel                                           |
//|------------------------------------------------------------------+
void SetInfoPanel()
  {
//--- Visualization or real time modes
   if(IsVisualMode() || IsRealtime())
     {
     // The remaining code of the SetInfoPanel() function
     // ...
     }
  }

Und jetzt müssen wir nur noch einige Veränderungen an den Haupt-Programmfunktionen vornehmen, damit wir zum nächsten Schritt gehen können - Optimierung von Paramtern und dem Testen des Expert Advisors.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialize the new bar
   CheckNewBar();
//--- Get the properties and set the panel
   GetPositionProperties(P_ALL);
//--- Set the info panel
   SetInfoPanel();
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- If the bar is not new, exit
   if(!CheckNewBar())
      return;
//--- If there is a new bar
   else
     {
      GetBarsData();          // Get bar data
      TradingBlock();         // Check the conditions and trade
      ModifyTrailingStop();   // Modify the Trailing Stop level
     }
//--- Get the properties and update the values on the panel
   GetPositionProperties(P_ALL);
//--- Update the info panel
   SetInfoPanel();
  }
//+------------------------------------------------------------------+
//| Trade event                                                      |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- Get position properties and update the values on the panel
   GetPositionProperties(P_ALL);
//--- Update the info panel
   SetInfoPanel();
  }


Optimierung von Parametern und Testen des Expert Advisors

Optimieren wir zunächst die Parameter. Die Einstellungen des Strategietesters legen wir wie folgt fest:

Abb. 1 Einstellungen des Strategietesters zur Optimierung von Parametern.

Abb. 1 Einstellungen des Strategietesters zur Optimierung von Parametern.

Die Parameter des Expert Advisors erhalten einen weit gefassten Wertebereich:

Abb. 2 Einstellungen des Expert Advisors zur Optimierung von Parametern.

Abb. 2 Einstellungen des Expert Advisors zur Optimierung von Parametern.

Die Optimierung hat auf einem Dual-Core Prozessor (Intel Core2 Duo P7350 @ 2.00GHz) ca. 7 Minuten gedauert. Die Testergebnisse für den maximalen Rückflusssfaktor finden Sie hier:

Abb. 3 Testergebnisse für den maximalen Rückflusssfaktor.

Abb. 3 Testergebnisse für den maximalen Rückflusssfaktor.


Fazit

Das ist jetzt erst einmal alles. Studieren Sie, testen Sie, Optimieren Sie, Probieren Sie aus - und Sei werden begeistert sein. Der Quellcode für den in diesem Beitrag besprochenen Expert Advisor kann mit Hilfe des unten stehenden Links für weitere Betrachtungen heruntergeladen werden.

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

Beigefügte Dateien |
MQL5 Cookbook: Die History der Abschlüsse und Funktions-Library zum Erhalt von Position-Eigenschaften MQL5 Cookbook: Die History der Abschlüsse und Funktions-Library zum Erhalt von Position-Eigenschaften

Eine gute Gelegenheit, die in den vorangegangenen Beiträgen zu Position-Eigenschaften beschriebenen Informationen nochmals kurz zusammenzufassen. In diesem Beitrag werden wir einige zusätzliche Funktionen erzeugen, um die Eigenschaften zu erhalten, die man nur nach Zugriff auf die History der Abschlüsse abrufen kann. Darüber hinaus lernen wir Datenstrukturen kennen, mit deren Hilfe wir auf Position- und Symboleigenschaften auf weitaus bequemere Weise zugreifen können.

MQL5 Cookbook: Handelsbedingungen mit Hilfe von Indikatoren in Experts Advisors einrichten MQL5 Cookbook: Handelsbedingungen mit Hilfe von Indikatoren in Experts Advisors einrichten

Auch in diesem Beitrag werden wir den Expert Advisor, den wir in allen vorangegangenen Beiträgen der MQL5 Cookbook Reihe bearbeitet haben, weiter verändern. Diesmal soll er durch Indikatoren verbessert werden mit Hilfe deren Werte nach Bedingungen zur Eröffnung von Positions gesucht werden kann. Um dem noch eins draufzusetzen, legen wir eine Dropdown-Liste in den externen Parametern an, um einen der drei Handels-Indikatoren auswählen zu können.

MQL5 Cookbook: Position-Eigenschaften im MetaTrader 5 Strategietester analysieren MQL5 Cookbook: Position-Eigenschaften im MetaTrader 5 Strategietester analysieren

Wir präsentieren hier eine veränderte Version des Expert Advisors aus dem vorangegangenen Beitrag "MQL5 Cookbook: Position-Eigenschaften auf dem Angepassten Info-Panel". Einige der Themen, die wir ansprechen werden, sind: Daten von Bars bekommen, nach neuen Bar-Ereignissen auf dem aktuellen Symbol suchen, eine Handelsklasse der Standard-Library in eine Datei aufnehmen, eine Suchfunktion für Handelssignale und eine Funktion zur Ausführung von Handelsoperationen erzeugen sowie Handelsereignisse in der OnTrade() Funktion festlegen.

MQL5 Cookbook: Position-Eigenschaften auf dem Angepassten Info-Panel MQL5 Cookbook: Position-Eigenschaften auf dem Angepassten Info-Panel

Diesmal erzeugen wir einen einfachen Expert Advisor, der die Position-Eigenschaften auf dem aktuellen Symbol abruft und sie im angepassten Info-Panel während manuell durchgeführtem Handel anzeigt. Das Info-Panel wird mit Hilfe graphischer Objekte erstellt, und die angezeigte Information wird bei jeder Kursschwankung (Tick) aktualisiert. Das ist weitaus bequemer als ständig das im vorangegangenen Beitrag der Reihe "MQL5 Cookbook: Wie man Position-Eigenschaften abruft", beschriebene Script manuell laufen lassen zu müssen.