Beschränkungen und Überprüfungen in Expert Advisors

MetaQuotes | 11 Januar, 2016

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:

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.

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.

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.

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.

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.