MQL5 Cookbook: Handelsbedingungen mit Hilfe von Indikatoren in Experts Advisors einrichten
Einleitung
In diesem Beitrag wird der Expert Advisor durch Indikatoren verbessert, 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.
Bitte denken Sie daran: Wir verändern weiterhin den Expert Advisor, den wir schon in allen vorangegangenen Beiträgen der MQL5 Cookbook Reihe bearbeitet haben. Die letzte Version des Expert Advisors kann aus dem Beitrag "MQL5 Cookbook: Die History der Abschlüsse und Funktions-Library zum Erhalt von Position-Eigenschaften" heruntergeladen werden.
Darüber hinaus präsentiert dieser Beitrag eine Funktion, die wir zur Prüfung erzeugen, ob Handelsoperationen durchgeführt werden können oder nicht. Die Funktion zur Eröffnung von Positions wird dahingehend verändert, dass sie es dem Expert Advisor ermöglicht, den Handelsmodus festzustellen (Sofortige Ausführung und Markt Ausführung).
Da der Code des Expert Advisors, infolge aller Verbesserungen und Erweiterungen aus den vorangegangenen Beiträgen bereits mehr als 1.500 Zeilen beträgt, wird er mit jedem neuen Feature, das hinzukommt, immer unbequemer und aufwendiger. Daher liegt es logischerweise nahe, ihn in mehrere Kategorien als separate Library-Dateien aufzuspalten. Unsere Ziele sind jetzt erklärt, also können wir anfangen.
Entwicklung des Expert Advisors
Den Quellcode des Expert Advisors (*.mq5) aus dem vorangegangenen Beitrag legen wir in einem extra Ordner ab, TestIndicatorConditions, in dem wir einen Unterordner 'Mit Einschließen' anlegen müssen. In diesem Ordner werden wir alle 'Mit Einschließen'-Dateien (*.mqh) anlegen. Sie können mit Hilfe des MQL5-Assistenten (Strg+N) oder manuell als Standard-Textdateien (*.txt) im erforderlichen Directory angelegt und später in *.mqh umbenannt werden.
Unten finden Sie alle Namen und Anmerkungen für alle erzeugten 'Mit Einschließen'-Dateien:
- Enums.mqh enthält die Aufzählungen;
- InfoPanel.mqh präsentiert Features zum Einrichten des Info-Panels und der Erzeugung und dem Löschen grafischer Objekte;
- Errors.mqh umfasst alle Funktionen zur Lieferung von Fehlercodes und Gründen für eine De-Initialisierung;
- TradeSignals.mqh präsentiert Funktionen, die Arrays mit Kursen und Indikatorwerten füllen, sowie einen Signalblock;
- TradeFunctions.mqh umfasst Handelsfunktionen;
- ToString.mqh bietet Funktionen zur Umwandlung von numerischen Werten in String-Werte;
- Auxiliary.mqh dient für andere Hilfsfunktionen.
Um diese Libraries in die Hauptdatei mit einzuschließen, verwenden wir die #include Direktive. Da sich die Hauptdatei des Expert Advisors und der Ordner mit den 'einzuschließenden' Dateien (Mit einschließen) im selben Ordner befinden, lautet der Code für die 'einzuschließenden' Datei wie folgt:
//--- Include custom libraries #include "Include\Enums.mqh" #include "Include\InfoPanel.mqh" #include "Include\Errors.mqh" #include "Include\TradeSignals.mqh" #include "Include\TradeFunctions.mqh" #include "Include\ToString.mqh" #include "Include\Auxiliary.mqh"
Danach können wir sie öffnen und verändern und einen Teil des Quellcodes aus der Hauptdatei des Expert Advisor verschieben.
Zur korrekten Navigation durch den Code, wird jede Header-Datei durch Verweise auf die daneben liegenden Header-Dateien und auch auf die Hauptdatei des Expert Advisors ergänzt. Für unsere Library an Handelsfunktionen, TradeFunctions.mqh, sieht das dann beispielsweise so aus:
//--- Connection with the main file of the Expert Advisor #include "..\TestIndicatorConditions.mq5" //--- Include custom libraries #include "Enums.mqh" #include "InfoPanel.mqh" #include "Errors.mqh" #include "TradeSignals.mqh" #include "ToString.mqh" #include "Auxiliary.mqh"
Für Dateien in derselben Verschachtelungsebene reicht es aus, einfach den Namen anzugeben. Um eine Ebene höher zu gelangen, müssen Sie nur zwei Punkte vor dem Backslash im Pfad machen.
In der Enums.mqh Datei fügen wird eine Aufzählung für Indikatoren hinzu. Zur besseren Veranschaulichung werden wir in diesem Expert Advisor mit zwei Standard-Indikatoren (Gleitender Mittelwert und Commodity Channel Index) und einem individuell angepassten Indikator (MultiRange_PCH) arbeiten. Die Aufzählung ist folgende:
//--- Indicators enum ENUM_INDICATORS { MA = 0, // Moving Average CCI = 1, // CCI PCH = 2 // Price Channel };
Die externen Parameter werden so verändert:
//--- External parameters of the Expert Advisor sinput long MagicNumber=777; // Magic number sinput int Deviation=10; // Slippage input ENUM_INDICATORS Indicator=MA; // Indicator input int IndicatorPeriod=5; // Indicator period input int IndicatorSegments=2; // Number of one direction indicator segments input double Lot=0.1; // Lot input double VolumeIncrease=0.1; // Position volume increase input double VolumeIncreaseStep=10; // Step for volume increase input double StopLoss=50; // Stop Loss input double TakeProfit=100; // Take Profit input double TrailingStop=10; // Trailing Stop input bool Reverse=true; // Position reversal sinput bool ShowInfoPanel=true; // Display of the info panel
Wie oben bereits angesprochen, können Sie aus der Dropdown-Liste des Indikator-Parameters einen von drei Indikatoren auswählen.
Es gibt nur einen Parameter, der auf alle Indikatoren anwendbar ist, indem der Indikator-Zeitraum eingerichtet werden kann - IndicatorPeriod. Der Parameter NumberOfBars aus der vorangegangenen Version des Expert Advisors ist in IndicatorSegments umbenannt worden und gibt nun die Anzahl der Bars an, während der ein gegebener Indikator nach oben/nach unten gehen muss, um die Bedingung zur Eröffnung eienr Position zu erfüllen
Des weiteren haben wir einen weiteren externen Parameter hinzugefügt: VolumeIncreaseStep. Mit seiner Hilfe kann man die Schritte zur Erhöhung des Volumens in Punkten setzen.
Der Wert der Variable AllowedNumberOfBars variable (heißt jetzt AllowedNumberOfSegments) ist in der individuell angepassten Funktion GetBarsData() angepasst worden. Er wird jetzt in eine separate Funktion platziert und nur bei ihrer Initialisierung aufgerufen.
Da die Bedingung zur Eröffnung einer Position nun mit Hilfe von Indikator-Werten geprüft wird, ist der Wert, der zugewiesen werden muss, immer um 2 größer. Mit anderen Worten: Wird der externen Variable IndicatorSegments der Wert "1" zugewiesen, wird der Variable AllowedNumberOfSegments der Wert "3" zugewiesen, da der Indikatorwert auf dem abgeschlossenen Bar immer größer sein muss als der auf dem vorigen Bar, da sonst die Bedingung (z.B. für KAUFEN) nicht erfüllt wird. Aus diesem Grund müssen wir die letzten drei Indikatorwerte bekommen.
Unten steht der CorrectInputParameters() Funktionscode:
//+------------------------------------------------------------------+ //| Adjusting input parameters | //+------------------------------------------------------------------+ void CorrectInputParameters() { //--- Adjust the number of bars for the position opening condition if(AllowedNumberOfSegments<=0) { if(IndicatorSegments<=1) AllowedNumberOfSegments=3; // At least three bars are required if(IndicatorSegments>=5) AllowedNumberOfSegments=5; // but no more than 7 else AllowedNumberOfSegments=IndicatorSegments+1; // and always greater by two } }
Bevor wir uns mit den Indikatoren beschäftigen, wollen wir eine Funktion erzeugen, die nachprüft, ob ein Handel überhaupt zugelassen ist. Sie heißt CheckTradingPermission(). Ist aus einem der in der Funktion aufgeführten Gründe kein Handel zugelassen, wird als Wert "0" geliefert. Das heißt, der nächste Versuch muss auf dem nächsten Bar erfolgen.
//+------------------------------------------------------------------+ //| Checking if trading is allowed | //+------------------------------------------------------------------+ bool CheckTradingPermission() { //--- For real-time mode if(IsRealtime()) { //--- Checking server connection if(!TerminalInfoInteger(TERMINAL_CONNECTED)) return(1); //--- Permission to trade at the running program level if(!MQL5InfoInteger(MQL5_TRADE_ALLOWED)) return(2); //--- Permission to trade at the terminal level if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return(3); //--- Permission to trade for the current account if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED)) return(4); //--- Permission to trade automatically for the current account if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT)) return(5); } //--- return(0); }
Doch kommen wir jetzt zum Hauptanliegen dieses Beitrags. Um auf die Werte des Indikators zugreifen zu können, müssen wir zunächst seinen Handle haben. Dies gelingt uns mit Hilfe spezieller Funktionen, der Namen aus dem Kürzel des Indikators und dem Symbol 'i' davor bestehen.
So lautet die entsprechende Funktion für den Indikator 'Gleitender Mittelwert' iMA(). Mit Hilfe dieser Funktionen erhält man alle Handles der Standard-Indikatoren im MetaTrader 5 Terminal. Eine vollständige Liste steht im Abschnitt der MQL5-Referenzhinweise mit der Überschrift Technische Indikatoren zur Verfügung. Wenn Sie also einen Handle eines individuell angepassten Indikators haben müssen, verwenden Sie die iCustom() Funktion.
Wir implementieren die GetIndicatorHandle() Funktion, wo, je nach dem im Indikator-Parameter ausgewählten Indikator, der Handle-Wert des entsprechenden Indikators der globalen Variable indicator_handle zugewiesen wird. Der Code der Funktion findet sich in unserer Library an Handelssignal-Funktionen (die \Include\TradeSignals.mqh Datei); die Variable mit dem Indikator-Handle befindet sich in der Hauptdatei des Expert Advisors.
//+------------------------------------------------------------------+ //| Getting the indicator handle | //+------------------------------------------------------------------+ void GetIndicatorHandle() { //--- If the Moving Average indicator is selected if(Indicator==MA) indicator_handle=iMA(_Symbol,Period(),IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE); //--- If the CCI indicator is selected if(Indicator==CCI) indicator_handle=iCCI(_Symbol,Period(),IndicatorPeriod,PRICE_CLOSE); //--- If the MultiRange_PCH indicator is selected if(Indicator==PCH) indicator_handle=iCustom(_Symbol,Period(),"MultiRange_PCH",IndicatorPeriod); //--- If the indicator handle could not be obtained if(indicator_handle==INVALID_HANDLE) Print("Failed to get the indicator handle!"); }
Des weiteren erzeugen wir die GetDataIndicators() Funktion, wo wir mit Hilfe der erhaltenen Indiaktor-Handles, ihre Werte bekommen können. Dies geschieht mit Hilfe der CopyBuffer() Funktion auf ganz ähnliche Weise, als wir mit Hilfe der CopyTime(), CopyClose(), CopyOpen(), CopyHigh() und CopyLow() Funktionen, die im Beitrag "MQL5 Cookbook: Position-Eigenschaften im MetaTrader 5 Strategietester analysieren" betrachtet wurden, unsere Bar-Werte erhalten haben.
Da der Indikator mehrere Puffer (Werte-Reihen) haben kann, wird der Puffer-Index an die CopyBuffer() Funktion als zweiter Parameter übertragen. Puffer-Indices für Standard-Indikatoren finden Sie in den MQL5-Referenzhinweisen. Puffer-Indices für individuell angepasste Indikatoren finden Sie im Code, vorausgesetzt der Quellcode ist vorhanden. Gibt es keinen Code, müssen Sie den Index durch Herumprobieren finden, indem Sie im Visualisierungsmodus des Strategietesters beobachten, wie Bedingungen erfüllt werden.
Doch zuvor müssen wir dynamische Arrays für Indikator-Pufferwerte in der Hauptdatei des Expert Advisors anlegen:
//--- Arrays for indicator values double indicator_buffer1[]; double indicator_buffer2[];
Der Code von GetIndicatorsData() steht unten:
//+------------------------------------------------------------------+ //| Getting indicator values | //+------------------------------------------------------------------+ bool GetIndicatorsData() { //--- If the indicator handle has been obtained if(indicator_handle!=INVALID_HANDLE) { //--- For the Moving Average or CCI indicator if(Indicator==MA || Indicator==CCI) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); //--- Get indicator values if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- For the MultiRange_PCH indicator if(Indicator==PCH) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); ArraySetAsSeries(indicator_buffer2,true); //--- Get indicator values if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments || CopyBuffer(indicator_handle,1,0,AllowedNumberOfSegments,indicator_buffer2)<AllowedNumberOfSegments) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 or indicator_buffer2 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- return(true); } //--- If the indicator handle has not been obtained, retry else GetIndicatorHandle(); //--- return(false); }
Die GetTradingSignal() Funktion wurde wesentlich verändert. Die Bedingungen unterscheiden sich wenn die Position nicht vorhanden ist und wenn die Position besteht. Für die Indikatoren Gleitender Mittelwert und CCI sind die Bedingungen dieselben. Für MultiRange_PCH sind sie in einem separaten Block angeordnet. Damit der Code besser lesbar wird und um Wiederholungen zu vermeiden, erzeugen wir eine Hilfsfunktion GetSignal(), die ein Signal zur Eröffnung oder Umkehrung eine Position liefert, vorausgesetzt so eine Position besteht und die entsprechende Handlung vom externen Parameter zugelassen wird.
Unten steht der Code der GetSignal() Funktion:
//+------------------------------------------------------------------+ //| Checking the condition and returning a signal | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetSignal() { //--- Check conditions for the Moving Average and CCI indicators if(Indicator==MA || Indicator==CCI) { //--- A Sell signal if(AllowedNumberOfSegments==3 && indicator_buffer1[1]<indicator_buffer1[2]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5] && indicator_buffer1[5]<indicator_buffer1[6]) return(ORDER_TYPE_SELL); //--- A Buy signal if(AllowedNumberOfSegments==3 && indicator_buffer1[1]>indicator_buffer1[2]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5] && indicator_buffer1[5]>indicator_buffer1[6]) return(ORDER_TYPE_BUY); } //--- Block that checks conditions for the MultiRange_PCH indicator if(Indicator==PCH) { //--- A Sell signal if(close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- A Buy signal if(close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); } //--- No signal return(WRONG_VALUE); }
Der Code der GetTradingSignal() Funktion sieht nun so aus:
//+------------------------------------------------------------------+ //| Determining trading signals | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetTradingSignal() { //--- If there is no position if(!pos.exists) { //--- A Sell signal if(GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- A Buy signal if(GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); } //--- If the position exists if(pos.exists) { //--- Get the position type GetPositionProperties(P_TYPE); //--- Get the last deal price GetPositionProperties(P_PRICE_LAST_DEAL); //--- Block that checks conditions for the Moving Average and CCI indicators if(Indicator==MA || Indicator==CCI) { //--- A Sell signal if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- A Buy signal if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } //--- Block that checks conditions for the MultiRange_PCH indicator if(Indicator==PCH) { //--- A Sell signal if(pos.type==POSITION_TYPE_BUY && close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- A Buy signal if(pos.type==POSITION_TYPE_SELL && close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } } //--- No signal return(WRONG_VALUE); }
Jetzt müssen wir uns nur noch mit den Modi 'Sofortige Ausführung' und 'Markt-Ausführung' beschäftigen, die Teil der Symboleigenschaften sind und den Code der Funktion OpenPosition() zur Eröffnung der Position entsprechend verändern. Die Modi mit ihren selbst erklärenden Namen finden Sie ebenfalls in den MQL5-Referenzhinweisen:
- Sofortige Ausführung
- Markt-Ausführung
Ich möchte Sie hier daran erinnern, dass Sie, wenn Sie im Modus Markt-Ausführung sind, keine Position mit den eingerichteten Stop Loss- und Take Profit-Stufen eröffnen können. Sie müssen zunächst eine Position eröffnen und sie dann durch Setzen der Stufen entsprechend verändern.
Fügen wir den Ausführungs-Modus der Struktur der Symboleigenschaften hinzu:
//--- Symbol properties struct symbol_properties { int digits; // Number of decimal places in the price int spread; // Spread in points int stops_level; // Stops level double point; // Point value double ask; // Ask price double bid; // Bid price double volume_min; // Minimum volume for a deal double volume_max; // Maximum volume for a deal double volume_limit; // Maximum permissible volume for a position and orders in one direction double volume_step; // Minimum volume change step for a deal double offset; // Offset from the maximum possible price for a transaction double up_level; // Upper Stop level price double down_level; // Lower Stop level price ENUM_SYMBOL_TRADE_EXECUTION execution_mode; // Execution mode };
Analog müssen wir die ENUM_SYMBOL_PROPERTIES Aufzählung
//--- Enumeration of position 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_EXECUTION_MODE = 13, S_ALL = 14 };
und die GetSymbolProperties() Funktion verändern:
case S_EXECUTION_MODE: symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //--- case S_ALL : symb.digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); symb.spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD); symb.stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); symb.point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); symb.ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),symb.digits); symb.bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),symb.digits); symb.volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); symb.volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); symb.volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT); symb.volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); symb.offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*symb.point),symb.digits); symb.up_level=NormalizeDouble(symb.ask+symb.stops_level*symb.point,symb.digits); symb.down_level=NormalizeDouble(symb.bid-symb.stops_level*symb.point,symb.digits); symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //---
Als Ergebnis sieht der Code der OpenPosition() Funktion so aus:
//+------------------------------------------------------------------+ //| Opening a position | //+------------------------------------------------------------------+ void OpenPosition(double lot, ENUM_ORDER_TYPE order_type, double price, double sl, double tp, string comment) { //--- Set the magic number in the trading structure trade.SetExpertMagicNumber(MagicNumber); //--- Set the slippage in points trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation)); //--- The Instant Execution mode // A position can be opened with the Stop Loss and Take Profit levels set if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT) { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } //--- The Market Execution mode // First open a position and only then set the Stop Loss and Take Profit levels // *** Starting with build 803, Stop Loss and Take Profit can be set upon position opening *** if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET) { //--- If there is no position, first open a position and then set Stop Loss and Take Profit if(!pos.exists) { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,0,0,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); //--- Get the flag of presence/absence of the position pos.exists=PositionSelect(_Symbol); //--- If the position exists if(pos.exists) { //--- Set Stop Loss and Take Profit if(!trade.PositionModify(_Symbol,sl,tp)) Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } //--- If the position exists, increase its volume and leave the Stop Loss and Take Profit levels unchanged else { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } }
Den Funktionen zum Umgang mit den Ereignissen: OnInit
- OnDeinit
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Adjust the input parameters CorrectInputParameters(); //--- Get indicator handles GetIndicatorHandle(); //--- Initialize the new bar CheckNewBar(); //--- Get the properties GetPositionProperties(P_ALL); //--- Set the info panel SetInfoPanel(); //--- return(0); }
- OnTick
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Print the deinitialization reason to the journal Print(GetDeinitReasonText(reason)); //--- When deleting from the chart if(reason==REASON_REMOVE) { //--- Delete all objects relating to the info panel from the chart DeleteInfoPanel(); //--- Delete the indicator handle IndicatorRelease(indicator_handle); } }
- müssen wir immer noch den letzten, sehr wichtigen Schliff verpassen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- If the bar is not new, exit if(!CheckNewBar()) { if(IsVisualMode() || IsRealtime()) { //--- Get the properties and update the values on the panel GetPositionProperties(P_ALL); //--- Set/update the info panel SetInfoPanel(); } return; } //--- If there is a new bar else { //--- If trading is allowed if(CheckTradingPermission()==0) { if(!GetIndicatorsData()) return; GetBarsData(); // Get bar data TradingBlock(); // Check the conditions and trade ModifyTrailingStop(); // Modify the Trailing Stop level } } //--- Get the properties GetPositionProperties(P_ALL); //--- Update the info panel SetInfoPanel(); }
Jetzt da alle Funktionen fertig sind, können wir die Parameter optimieren. Vergessen Sie nicht, dass Sie den Code aus der Haupt-Programmdatei erstellen müssen.
Optimierung von Parametern und Testen des Expert Advisors
Der Strategietester muss so, wie unten abgebildet, eingerichtet werden:
Abb. 1 Einstellungen des Strategietesters.
Des Weiteren reichten wir die Parameter des Expert Advisors für eine Optimierung ein (s. auch die angehängte *.set-Datei mit ihren Einstellungen):
Abb. 2 Einstellungen des Expert Advisors.
Die Optimierung hat auf einem Dual-Core Prozessor ca. 40 Minuten gedauert. Mit Hilfe des Optimierungs-Charts können Sie teilweise die Qualität des Handelssystems auf Basis der Ergebnisse im Gewinnbereich abschätzen:
Abb. 3 Optimierungs-Chart.
Die Testergebnisse des maximalen Rückflussfaktors sind wie folgt:
Abb. 4 Testergebnisse des maximalen Rückflussfaktors.
Fazit
Die Download-Version des Archivs mit den Quellcodes des Expert Advisors ist an diesen Artikel angehängt. Sobald Sie es extrahiert haben, müssen Sie den Dateiordner \TestIndicatorConditions in <Metatrader 5 terminal>\MQL5\Experts platzieren. Zur Sicherstellung der korrekten Funktionsweise des Expert Advisors sollte der IndikatorMultiRange_PCH heruntergeladen und in <Metatrader 5 terminal>\MQL5\Indicators platziert werden.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/645
- 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.