Beschränkungen und Überprüfungen in Expert Advisors
Einleitung
Bei der Erstellung eines Algorithmus für automatischen Handel sollten Sie nicht nur die Preise für die Schaffung von Handelssignalen verarbeiten können, sondern auch in der Lage sein, zahlreiche zusätzliche Informationen über Beschränkungen der Arbeit von Expert Advisors einzuholen. In diesem Beitrag erfahren Sie, wie Sie:
- Informationen über Handelssitzungen erhalten;
- prüfen, ob Sie über ausreichendes Guthaben für die Eröffnung einer Position verfügen;
- eine Beschränkung für das Gesamt-Handelsaufkommen über ein Symbol definieren;
- eine Beschränkung für die Gesamtzahl von Aufträgen definieren;
- den potenziellen Verlust zwischen dem Ausgangspreis und Stop Loss berechnen;
- prüfen, ob es ein neues Bar gibt.
Handels- und Gebotssitzungen
Um diese Informationen über Handelssitzungen zu erhalten, sollten Sie die SymbolInfoSessionTrade()-Funktion nutzen. Für Gebotssitzungen nutzen Sie entsprechend die SymbolInfoSessionQuote()-Funktion. Beide Funktionen haben das gleiche Funktionsprinzip: Gibt es eine Sitzung mit dem angegebenen Index für den angegebenen Wochentag (die Indizierung von Sitzungen beginnt bei Null), gibt die Funktion true aus. Die Anfangs- und Endzeit einer Sitzung wird in den vierten und fünften über den Link übergebenen Parameter geschrieben.
//--- check if there is a quotation session with the number session_index bool session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish);Um alle Sitzungen des angegebenen Tages herauszufinden, rufen Sie diese Funktion in einer Schleife auf, bis sie false ausgibt.
//+------------------------------------------------------------------+ //| Display information about quotation sessions | //+------------------------------------------------------------------+ void PrintInfoForQuoteSessions(string symbol,ENUM_DAY_OF_WEEK day) { //--- start and end of session datetime start,finish; uint session_index=0; bool session_exist=true; //--- go over all sessions of this day while(session_exist) { //--- check if there is a quotation session with the number session_index session_exist=SymbolInfoSessionQuote(symbol,day,session_index,start,finish); //--- if there is such session if(session_exist) { //--- display the day of week, the session number and the time of start and end Print(DayToString(day),": session index=",session_index," start=", TimeToString(start,TIME_MINUTES)," finish=",TimeToString(finish-1,TIME_MINUTES|TIME_SECONDS)); } //--- increase the counter of sessions session_index++; } }
Der Wochentag wird im String-Format über die benutzerdefinierte Funktion DayToString() angezeigt, die den Wert der Aufzählung ENUM_DAY_OF_WEEK als Parameter erhält.
//+------------------------------------------------------------------+ //| Receive the string representation of a day of week | //+------------------------------------------------------------------+ string DayToString(ENUM_DAY_OF_WEEK day) { switch(day) { case SUNDAY: return "Sunday"; case MONDAY: return "Monday"; case TUESDAY: return "Tuesday"; case WEDNESDAY: return "Wednesday"; case THURSDAY: return "Thursday"; case FRIDAY: return "Friday"; case SATURDAY: return "Saturday"; default: return "Unknown day of week"; } return ""; }
Der endgültige Code des Scripts SymbolInfoSession.mq5 ist unten an diesen Beitrag angehängt. Sehen wir uns hier nur seinen Hauptteil an.
void OnStart() { //--- the array where the days of week are stored ENUM_DAY_OF_WEEK days[]={SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY}; int size=ArraySize(days); //--- Print("Quotation sessions"); //--- go over all the days of week for(int d=0;d<size;d++) { PrintInfoForQuoteSessions(Symbol(),days[d]); } //--- Print("Trading sessions"); //--- go over all the days of week for(int d=0;d<size;d++) { PrintInfoForTradeSessions(Symbol(),days[d]); } }
Prüfen der Marge
Um die notwendige Marge zum Öffnen oder Erhöhen einer Position herauszufinden, können Sie die Funktion OrderCalcMargin() nutzen. Der erste Parameter, der an sie übergeben wird, ist ein Wert der Aufzählung ENUM_ORDER_TYPE. Für einen Kaufvorgang sollten Sie sie mit dem Parameter ORDER_TYPE_BUY aufrufen. Zum Verkaufen nutzen Sie den Parameter ORDER_TYPE_SELL. Die Funktion gibt die Höhe der Marge in Abhängigkeit von der Anzahl von Posten und dem Eröffnungspreis aus.
void OnStart() { //--- the variable to receive the value of margin double margin; //--- to receive information about the last tick MqlTick last_tick; //--- try to receive the value from the last tick if(SymbolInfoTick(Symbol(),last_tick)) { //--- reset the last error code ResetLastError(); //--- calculate margin value bool check=OrderCalcMargin(type,Symbol(),lots,last_tick.ask,margin); if(check) { PrintFormat("For the operation %s %s %.2f lot at %G required margin is %.2f %s",OrderTypeToString(type), Symbol(),lots,last_tick.ask,margin,AccountInfoString(ACCOUNT_CURRENCY)); } } else { Print("Unsuccessful execution of the SymbolInfoTick() function, error ",GetLastError()); } }
Es sollte beachtet werden, dass die Funktion OrderCalcMargin() die Berechnung der Marge nicht nur für Marktaufträge ermöglicht, sondern auch für ausstehende Aufträge. Mithilfe des Scripts Check_Money.mq5 können Sie die Werte, die für alle Typen von Aufträgen ausgegeben werden, überprüfen.
Die Funktion OrderCalcMargin() ist für die Berechnung der Höhe der Marge für ausstehende Aufträge gedacht, da eine finanzielle Absicherung in einigen Handelssystemen auch für ausstehende Aufträge erforderlich sein kann. Für gewöhnlich wird die Höhe der Marge für ausstehende Aufträge über einen Koeffizienten zur Höhe der Marge für Long und Short Positions berechnet.
Identifikator |
Beschreibung |
Typ der Eigenschaft |
SYMBOL_MARGIN_LONG |
Höhe der Margenerhebung für Long Positions |
double |
SYMBOL_MARGIN_SHORT |
Höhe der Margenerhebung für Short Positions |
double |
SYMBOL_MARGIN_LIMIT |
Höhe der Margenerhebung für Limit-Aufträge |
double |
SYMBOL_MARGIN_STOP |
Höhe der Margenerhebung für Stop-Aufträge |
double |
SYMBOL_MARGIN_STOPLIMIT |
Höhe der Margenerhebung für Stop-Limit-Aufträge |
double |
Sie können die Werte dieser Koeffizienten mit dem folgenden simplen Code abrufen:
//--- Calculate the rates of margin charging for different types of orders PrintFormat("Rate of margin charging on long positions is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LONG)); PrintFormat("Rate of margin charging on short positions is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_SHORT)); PrintFormat("Rate of margin charging on Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_LIMIT)); PrintFormat("Rate of margin charging on Stop orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOP)); PrintFormat("Rate of margin charging on Stop Limit orders is equal to %G",SymbolInfoDouble(Symbol(),SYMBOL_MARGIN_STOPLIMIT));
Bei Forex-Symbolen entspricht die Höhe der Margenerhebung für ausstehende Aufträge für gewöhnlich 0, d. h. sie haben keine Margenanforderungen.
Die Ergebnisse der Ausführung des Scripts Check_Money.mq5.
Je nach Art der Margenerhebung kann sich das System der Geldverwaltung ändern. Auch beim Handelssystem selbst kann es zu bestimmten Einschränkungen kommen, wenn für ausstehende Aufträge eine Marge benötigt wird. Deshalb können diese Parameter auch natürliche Einschränkungen des Betriebs eines Expert Advisors darstellen.
Berücksichtigung möglicher Gewinne und Verluste
Bei der Einrichtung eines Notstopps sollten Sie auf seine Auslösung eingestellt sein. Das Risiko eines potenziellen finanziellen Verlusts sollte berücksichtigt werden. Die Funktion OrderCalcProfit() dient diesem Zweck. Sie ist der bereits erwähnten Funktion OrderCalcMargin() sehr ähnlich, doch sie erfordert für ihre Berechnungen sowohl Eröffnungs- als auch Schließungspreise.
Legen Sie einen von zwei Werten der Aufzählung ENUM_ORDER_TYPE als ersten Parameter fest, ORDER_TYPE_BUY oder ORDER_TYPE_SELL. Andere Auftragstypen führen zu Fehlern. Im letzten Parameter muss eine Variable mithilfe der Referenz übergeben werden, in die die Funktion OrderCalcProfit() bei erfolgreicher Ausführung den Wert des Gewinns/Verlusts schreiben wird.
Ein Beispiel für die Verwendung der Funktion CalculateProfitOneLot(), die den Gewinn oder Verlust berechnet, wenn eine Long Position mit festgelegten Ein- und Ausgabewerten geschlossen wird:
//+------------------------------------------------------------------+ //| Calculate potential profit/loss for buying 1 lot | //+------------------------------------------------------------------+ double CalculateProfitOneLot(double entry_price,double exit_price) { //--- receive the value of profit to this variable double profit=0; if(!OrderCalcProfit(ORDER_TYPE_BUY,Symbol(),1.0,entry_price,exit_price,profit)) { Print(__FUNCTION__," Failed to calculate OrderCalcProfit(). Error ",GetLastError()); } //--- return(profit); }
Das Ergebnis der Berechnung dieser Funktion wird in der Abbildung dargestellt.
Ein Beispiel für die Berechnung und Anzeige des möglichen Verlusts im Diagramm mithilfe der OrderCalcProfit()-Funktion.
Den vollständigen Code finden Sie im angehängten Expert Advisor CalculateProfit_EA.mq5.
Prüfen, ob es ein neues Bar gibt
Bei der Entwicklung vieler Handelssysteme wird angenommen, dass die Handelssignale nur dann berechnet werden, wenn ein neues Bar erscheint, und dass alle Handelstätigkeiten nur einmal ausgeführt werden. Der Modus "Only open prices" (Nur Eröffnungspreise) des Strategietesters im MetaTrader 5 Client Terminal ist gut für die Überprüfung solcher automatisierten Handelssysteme geeignet.
Im Modus "Open prices only" werden alle Berechnungen von Indikatoren und der Aufruf der OnTick()-Funktion im Expert Advisor während des Testens nur einmal pro Bar durchgeführt. Dies ist der schnellste Handelsmodus und ist grundsätzlich die Art, Handelssysteme zu erstellen, die die größte Fehlertoleranz für unbedeutende Preisschwankungen hat. Natürlich sollten Indikatoren, die in einem Expert Advisor benutzt werden, korrekt geschrieben sein und ihre Werte nicht verzerrt werden, wenn ein neues Bar erscheint.
Mit dem Strategietester im Modus "Open prices only" müssen Sie sich keine Sorgen darüber machen, dass der Expert Advisor nur einmal pro Bar gestartet wird, was sehr bequem ist. Doch während der Arbeit im Echtzeitmodus an einem Demonstrations- oder echten Konto sollten Händler die Aktivitäten ihres Expert Advisors kontrollieren, damit dieser nur eine Handelstätigkeit pro eingehendes Signal durchführt. Am einfachsten wird dies durch die Überwachung der Eröffnungszeit des aktuellen unvollständigen Bars bewerkstelligt.
Um die Eröffnungszeit des letzten Bars abzurufen, sollten Sie die Funktion SeriesInfoInteger() mit angegebenem Symbolnamen, Zeitraum und der Eigenschaft SERIES_LASTBAR_DATE nutzen. Durch einen konstanten Vergleich der Eröffnungszeit des aktuellen Bars mit der Zeit des in der Variable gespeicherten Bars können Sie den Moment, in dem ein neues Bar erscheint, einfach herausfinden. Dies ermöglicht die Erstellung der benutzerdefinierten Funktion isNewBar(), die wie folgt aussehen kann:
//+------------------------------------------------------------------+ //| Return true if a new bar appears for the symbol/period pair | //+------------------------------------------------------------------+ bool isNewBar() { //--- remember the time of opening of the last bar in the static variable static datetime last_time=0; //--- current time datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE); //--- if it is the first call of the function if(last_time==0) { //--- set time and exit last_time=lastbar_time; return(false); } //--- if the time is different if(last_time!=lastbar_time) { //--- memorize time and return true last_time=lastbar_time; return(true); } //--- if we pass to this line then the bar is not new, return false return(false); }
Ein Beispiel für die Verwendung der Funktion finden Sie im angehängten Expert Advisor CheckLastBar.mq5.
Die Meldungen des Expert Advisors CheckLastBar zum Erscheinen neuer Bars im Zeitrahmen M1.
Beschränkung der Anzahl ausstehender Aufträge
Wenn Sie die Anzahl aktiver ausstehender Aufträge beschränken müssen, die gleichzeitig für ein Konto platziert werden können, können Sie Ihre eigene benutzerdefinierte Funktion schreiben. Nennen wir sie IsNewOrderAllowed(). Sie prüft, ob das Platzieren eines weiteren ausstehenden Auftrags erlaubt ist. Schreiben wir sie so, dass sie die Regeln der Meisterschaft des automatisierten Handels erfüllt.
//+------------------------------------------------------------------+ //| Checks if it is allowed to place another order | //+------------------------------------------------------------------+ bool IsNewOrderAllowed() { //--- get the allowed number of pending orders on an account int max_allowed_orders=(int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS); //--- if there is no limitations, return true; you can send an order if(max_allowed_orders==0) return(true); //--- if we pass to this line, then there are limitations; detect how many orders are already active int orders=OrdersTotal(); //--- return the result of comparing return(orders<max_allowed_orders); }
Die Funktion ist einfach: Rufen Sie die erlaubte Anzahl von Aufträgen mit der Variable max_allowed_orders ab. Ist dieser Wert nicht gleich Null, vergleichen Sie ihn mit der aktuellen Anzahl von Aufträgen. Allerdings berücksichtigt diese Funktion eine weitere mögliche Einschränkung nicht: die Beschränkung der erlaubten Gesamtmenge offener Positionen und ausstehender Aufträge nach bestimmtem Symbol.
Beschränkung der Anzahl von Posten nach bestimmtem Symbol
Um die Größe einer offenen Position nach einem bestimmten Symbol zu erhalten, müssen Sie zuerst mithilfe der Funktion PositionSelect() eine Position auswählen. Erst danach können Sie mithilfe der PositionGetDouble()-Funktion das Volumen der offenen Position anfragen. Diese gibt diverse Eigenschaften der ausgewählten Position aus, die den Typ double haben. Lassen Sie uns die PositionVolume()-Funktion schreiben, um das Positionsvolumen nach einem bestimmten Symbol zu erhalten.
//+------------------------------------------------------------------+ //| Returns the size of position by a specific symbol | //+------------------------------------------------------------------+ double PositionVolume(string symbol) { //--- try to select a positions by a symbol bool selected=PositionSelect(symbol); //--- the position exists if(selected) //--- return the position volume return(PositionGetDouble(POSITION_VOLUME)); else { //--- report about the unsuccessful attempt to select the position Print(__FUNCTION__," Failed to execute PositionSelect() for the symbol ", symbol," Error ",GetLastError()); return(-1); } }
Bevor wir eine Handelsanfrage für das Platzieren eines ausstehenden Auftrags nach einem Symbol machen, sollten Sie die Beschränkung des Gesamtvolumens der offenen Position und ausstehende Aufträge nach diesem Symbol überprüfen – SYMBOL_VOLUME_LIMIT. Gibt es keine Beschränkung, kann das Volumen eines ausstehenden Auftrags nicht das maximale erlaubte Volumen überschreiten, das mithilfe der Funktion SymbolInfoDouble() abgerufen werden kann.
double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT); if(max_volume==0) volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
Allerdings berücksichtigt diese Vorgehensweise nicht das Volumen aktueller ausstehender Aufträge nach dem bestimmten Symbol. Schreiben wir eine Funktion, die diesen Wert berechnet:
//+------------------------------------------------------------------+ //| Returns the size of position by a specified symbol | //+------------------------------------------------------------------+ double PositionVolume(string symbol) { //--- try to select a position by a symbol bool selected=PositionSelect(symbol); //--- the position exist if(selected) //--- return the position volume return(PositionGetDouble(POSITION_VOLUME)); else { //--- return zero if there is no position return(0); } }
Unter Berücksichtigung des Volumens der offenen Position und des Volumens ausstehender Aufträge sieht die finale Überprüfung so aus:
//+------------------------------------------------------------------+ //| Returns maximum allowed volume for an order by a symbol | //+------------------------------------------------------------------+ double NewOrderAllowedVolume(string symbol) { double allowed_volume=0; //--- get the limitation on the maximum volume of an order double symbol_max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX); //--- get the limitation of volume by a symbol double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT); //--- get the volume of open position by a symbol double opened_volume=PositionVolume(symbol); if(opened_volume>=0) { //--- if we already used available volume if(max_volume-opened_volume<=0) return(0); //--- volume of the open position doen't exceed max_volume double orders_volume_on_symbol=PendingsVolume(symbol); allowed_volume=max_volume-opened_volume-orders_volume_on_symbol; if(allowed_volume>symbol_max_volume) allowed_volume=symbol_max_volume; } return(allowed_volume); }
Der vollständige Code des Expert Advisors Check_Order_And_Volume_Limits.mq5, der die in diesem Abschnitt erwähnten Funktionen enthält, ist an den Beitrag angehängt.
Ein Beispiel für die Überprüfung mithilfe des Expert Advisors Check_Order_And_Volume_Limits auf dem Konto eines Teilnehmers des Automated Trading Championship 2010.
Überprüfen der Korrektheit des Volumens
Ein wichtiger Bestandteil jedes Handelsroboters ist die Fähigkeit, ein korrektes Volumen für die Durchführung einer Handelstätigkeit auszuwählen. Wir werden an dieser Stelle nicht über Systeme für Geld- und Risikomanagement sprechen, sondern über das korrekte Volumen gemäß den entsprechenden Eigenschaften eines Symbols.
Identifikator |
Beschreibung |
Typ der Eigenschaft |
SYMBOL_VOLUME_MIN |
Mindestvolumen für einen Abschluss |
double |
SYMBOL_VOLUME_MAX |
Höchstvolumen für einen Abschluss |
double |
SYMBOL_VOLUME_STEP |
Minimaler Volumenänderungsschritt für die Durchführung des Abschlusses |
double |
Zur Durchführung einer solchen Überprüfung können wir die benutzerdefinierte Funktion CheckVolumeValue() schreiben:
//+------------------------------------------------------------------+ //| Check the correctness of volume of an order | //+------------------------------------------------------------------+ bool CheckVolumeValue(double volume,string &description) { //--- Minimum allowed volume for trade operations double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN); if(volume<min_volume) { description=StringFormat("Volume is less than the minimum allowed SYMBOL_VOLUME_MIN=%.2f",min_volume); return(false); } //--- Maximum allowed volume for trade opertations double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX); if(volume>max_volume) { description=StringFormat("Volume is greater than the maximum allowed SYMBOL_VOLUME_MAX=%.2f",max_volume); return(false); } //--- get the minimal volume change step double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP); int ratio=(int)MathRound(volume/volume_step); if(MathAbs(ratio*volume_step-volume)>0.0000001) { description=StringFormat("Volume is not a multiple of minimal step SYMBOL_VOLUME_STEP=%.2f, the closest correct volume is %.2f", volume_step,ratio*volume_step); return(false); } description="Correct value of volume "; return(true); }
Sie können mithilfe des an diesen Beitrag angehängten Scripts CheckVolumeValue.mq5 überprüfen, ob diese Funktion funktioniert.
Meldungen des Scripts CheckVolumeValue.mq5, das die Korrektheit des Volumens prüft.
Fazit
Dieser Beitrag beschreibt die grundlegenden Überprüfungen auf potenzielle Beschränkungen der Funktionsfähigkeit eines Expert Advisors, die bei der Erstellung Ihres eigenen automatisierten Handelssystems auftreten können. Diese Beispiele berücksichtigen nicht alle möglichen Bedingungen, die während des Betriebs eines Expert Advisors auf einem Handelskonto überprüft werden sollten. Ich hoffe allerdings, dass diese Beispiele Neueinsteigern helfen, zu verstehen, wie die gängigsten Überprüfungen in der MQL5-Sprache implementiert werden.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/22
- 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.