
MQL5 Cookbook: Wie man bei der Einrichtung/Änderung von Handelsstufen Fehler vermeidet
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.
Die Parameter des Expert Advisors erhalten einen weit gefassten Wertebereich:
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.
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 Ltd.
Originalartikel: https://www.mql5.com/ru/articles/643





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.