MetaTrader 5 herunterladen

Sichern Ihres Expert Advisors beim Handel auf der Moskauer Börse

27 Juni 2016, 12:04
Vasiliy Sokolov
0
342

Inhaltsverzeichnis


Einleitung

Jeder, der auf den Finanzmärkten handelt, riskiert finanzielle Verluste. Die Natur dieser Risiken mag sich unterscheiden, doch das Ergebnis ist immer gleich – verlorenes Geld, verschwendete Zeit und anhaltender Frust. Um all das zu vermeiden, sollte man einige einfache Regeln befolgen: die Kontrolle über die Risiken behalten (Kapitelverwaltung), zuverlässige Handelsalgorithmen entwickeln und rentable Handelssysteme nutzen. Diese Regeln beziehen sich auf unterschiedliche Bereiche des Handels und wir sollten sie kombinieren, sodass wir auf zuverlässige positive Handelsergebnisse hoffen können.

Man findet derzeit zahlreiche Bücher und Artikel über die Fragen der Kapitalverwaltung und über Handelssysteme, die sich für alltägliche Handelsaktivitäten nutzen lassen. Leider gilt das nicht für Arbeiten über simple Sicherheitsgrundlagen des Handels auf den Märkten.

Mit diesem Beitrag soll sich das ändern, indem er die erwähnten Sicherheitsregeln beschreibt, die beim Handel auf den Märkten befolgt werden sollten. Diese Regeln bestehen aus Methoden und Handelspraktiken, die es Ihnen ermöglichen, bedeutende finanzielle Verluste durch Preisanstiege, fehlende Liquidität und sonstige höhere Gewalt zu vermeiden. Dieser Beitrag konzentriert sich auf technische Risiken und geht nicht auf die Themen der Entwicklung von Handelsstrategien und Risikomanagement ein.

Er liefert einen praktischen Ansatz für die Handelstheorie aus dem Beitrag Grundlagen der Preisbildung von Börsen anhand des Beispiels des Terminmarktes der Moskauer Börse. Während der erwähnte Beitrag sich der Theorie der Preisbildung von Börsen widmet, beschreibt der hier vorliegende Beitrag die Mechanismen, die Sie und Ihren Expert Advisor vor versehentlichen finanziellen Einbrüchen in Folge von gefährlichen Momenten der Preisbildung von Börsen schützen.


KAPITEL 1 Diskretheit des Preisflusses und der Umgang damit


1,1 Diskretheit des Preisflusses. Preislücken

Liquidität ist eines der Hauptkonzepte auf der Börse. Sie bildet die Fähigkeit des Marktes, Waren zu Preisen, die nahe an den Marktpreisen liegen, von Ihnen zu kaufen und Ihnen zu verkaufen. Je höher die Liquidität des Marktes, desto näher folgen die Marktpreise einander. Die Preisbildung ist ein diskreter Prozess. Das bedeutet, dass der von uns verwendete Preis aus mehreren Transaktionen besteht, die mit hoher Geschwindigkeit aufeinander folgen. Der Fluss der Transaktionen wird durch Gebote, oder Tick-Diagramme, geformt, die anschließend in Kerzen- oder Balkendiagrammen eines beliebigen Timeframes neu angeordnet werden. Aus der Perspektive eines Händlers sind solche Diagramme unterbrechungsfrei. Ein Balken oder eine Kerze haben zu jedem beliebigen Zeitpunkt einen bestimmten Preis. Dies lässt sich folgendermaßen darstellen:


Abb. 1. Preisbalken und dessen unterbrechungsfreie Preisfunktion

Ganz egal, welchen Punkt des Preisbalkens wir nehmen, hat er stets sein eigenes, durch eine rote Linie dargestelltes Preisniveau. Auf genau diese Weise werden Balken im Modus "Alle Ticks" des MetaTrader-Strategietesters dargestellt. In diesem Modus werden Preise unterbrechungsfrei und sequenziell generiert. Beträgt beispielsweise die Schrittweite 1 und der Preis hat sich von 10 bis 15 bewegt, sind die Preise 11, 12, 13 und 14 für uns während der Preisbewegung ebenfalls verfügbar. In Wirklichkeit ist der Preis diskret und verändert sich in kleinen Sprüngen. Außerdem sind diese Preisänderungen nicht zwangsläufig konsistent und gleichmäßig. Manchmal überspringt der Preis gleich mehrere Ebenen. Lassen Sie uns den gleichen Balken mithilfe einer realistischeren (diskreten) Art der Preisänderung untersuchen:

Abb. 2 Preisbalken und dessen diskrete Preisfunktion

Wie wir sehen können, gibt es so etwas wie einen unterbrechungsfreien Preis nicht (hier als gestrichelte rote Linie dargestellt). Das bedeutet, dass Ihre Marktorder (insbesondere Stops) bei einem unbeabsichtigten Preis ausgelöst werden können! Dies ist eine äußerst gefährliche Eigenschaft von Marktordern. Sehen wir uns an, wie eine Pending Order des Typen Buy Stop auf diesem Balken ausgelöst werden kann. Nehmen wir an, wir senden eine Marktanfrage, wenn der Preis ein Niveau von 64.203 oder höher erreicht (blaue gestrichelte Linie, die den Preisbalken überkreuzt). Es ist allerdings möglich, dass dieser Preis innerhalb des Balkens überhaupt nicht existiert. In diesem Fall wird unsere Order beim nächsten Preis aktiviert, der wesentlich höher als 64.203 ist:

Abb. 3 Aktivierung von Pending Orders bei diskreten Preisen

In unserem Beispiel findet die tatsächliche Ausführung der Order erst bei 64.220 Punkten statt, was 13 Punkte schlechter ist als der von uns angefragte Preis. Die Differenz zwischen diesen Preisen formt eine Slippage. Wenn ein Markt ausreichend liquide ist, gehen diskrete Preise in einem dichten Strom ein, der reibungslos von einer Ebene zur nächsten fließt. Wenn sich der Preis allerdings schnell ändert, können auch auf hochliquiden Märkten Preislücken entstehen. Bei einer Betrachtung von herkömmlichen Preisdiagrammen ist es unmöglich, diese Lücken zu sehen, doch wir müssen uns ihr Vorhandensein bewusst machen.


1,2 Preissprünge

Bei mangelnder Liquidität können Preislücken sehr hohe Werte annehmen und zu Preissprüngen werden (Abschlüsse, die bei Preisen getätigt werden, die zu stark von den Marktpreisen abweichen). Diese sind sowohl für Händler als auch für automatisierte Handelssysteme äußerst gefährlich. Solche Sprünge lösen Pending-Stop-Order aus, die bei sehr ungünstigen Preisen ausgeführt werden.

Betrachten wir einen einfachen Fall: Nehmen wir an, wir haben einen RUB/USD-Terminkontrakt und platzieren eine Buy-Stop-Order zum Kauf bei 64.200. Der Stop Loss wird bei 64.100 platziert. Wir erwarten, dass der Preis steigt, doch wenn das nicht passiert, soll unser Stop Loss bei 64.100 unseren Verlust um 100 Punkte begrenzen. Unser Risiko scheint begrenzt zu sein, doch das entspricht nicht der Realität. Betrachten wir einen Fall, in dem ein Preissprung stattfindet, der unsere Stop-Order bei ganz anderen Preisen aktiviert:


Abb. 4 Tick-Darstellung eines Sprungs und Ausführung der Buy-Stop-Order

In diesem Tick-Diagramm können wir sehen, dass einer der Ticks zu weit von den anderen entfernt ist und eine Spitze bildet. Dieser Tick löst unsere Buy-Stop-Order bei 64.440 aus. Beim nächsten Tick kehrt der Preis in seinen aktuellen Bereich zurück und löst unsere Stop-Order bei 64.100 aus. In weniger als einer Sekunde kann unsere Pending Order ausgelöst und durch einen Stop Loss geschlossen werden, wodurch wir einen großen Verlust erleiden. Anstatt unseres berechneten Verlusts von 100 Punkten verliert unsere Order 340 Punkte.

In der Realität können solche Sprünge noch viel größer sein. Also ist ein einziger großer Sprung bereits genug, um unser Konto zu ruinieren, ganz egal, wie groß es ist! Um solche Katastrophen zu vermeiden, müssen Sie die simplen Schutzregeln befolgen, die weiter unten dargelegt sind.

Beachten Sie, dass im Modus "Alle Ticks" des Strategietesters solche Sprünge bei simulierten Preisen stattfinden, die sich als besser erweisen können als auf dem realen Markt. Wenn wir unsere Strategie auf dem in der Abbildung dargestellten Preisintervall getestet hätten, hätte unsere Pending Order höchstens eine geringe Slippage. Wie wir wissen, ist der Preisfluss innerhalb eines Balkens im Strategietester weitestgehend unterbrechungsfrei. Somit führt der Tester unsere Order bei einem Preis aus, der nahe an dem Preis liegt, der durch die Order ohne Slippage festgelegt wurde. Tatsächlich können solche Fälle im Strategietester eingehend betrachtet werden. Dafür müssen Sie einen speziellen Testmodus auswählen. Darauf gehen wir im entsprechenden Abschnitt in Kapitel 3 näher ein. 


1,3 Umgang mit der maximalen Slippage mithilfe von Limit-Ordern

Wir haben herausgefunden, dass Markt- und Stop-Order nicht gegen Slippage geschützt sind. Möglicherweise reicht die Liquidität nicht aus, um unsere Anfrage auszuführen, oder der Markt kann seine Marktliquidität für einen kurzen Zeitraum verlieren, was zu Preissprüngen führt. Zudem treten solche Sprünge bei Märkten mit geringer Liquidität, z. B. dem FORTS-Terminmarkt, regelmäßig auf. Allerdings können Sie solche Sprünge durch die Verwendung von Limit-Ordern anstelle von Markt- und Stop-Ordern verhindern.

Eine Limit-Order wird immer bei einem Preis ausgeführt, der nicht schlechter ist als der angegebene. Ein interessantes Merkmal von Limit-Ordern im Börsenmodus ist ihre Ausführbarkeit beim aktuellen Preis, auch wenn sein Niveau über oder unter dem festgelegten Orderpreis liegt.

Beträgt der aktuelle Preis des RUB/USD-Terminkontrakts beispielsweise 64.200, können wir eine Buy-Limit-Order bei 64.220 festlegen. Das bedeutet, dass wir einem Kauf zustimmen, sofern der Preis nicht höher als 64.220 ist. Da der aktuelle Preis (64.200) besser als der in der Order festgelegte ist, wird unsere Order sofort ausgeführt, nachdem sie platziert wurde. Somit können wir den maximalen Slippage-Wert kontrollieren. Wenn aus irgendeinem Grund keine ausreichende Liquidität für das Niveau von 64.220 vorhanden ist, wird ein Teil unserer Order einfach nicht ausgeführt.

Beachten Sie, dass Sie die Slippage nur durch die Verwendung von Limit-Ordern kontrollieren können. Herkömmliche Marktorder im Börsenmodus ermöglichen keine Festlegung des maximalen Slippage-Niveaus. Deshalb liefern Limit-Order die einzige Möglichkeit, die Sicherheit beim Handel auf Märkten mit geringer Liquidität beizubehalten.

Es ist nur vernünftig, Limit-Order für Marktein- und -austritte zu nutzen. Sie können sie platzieren, auch wenn Ihre Strategie einen Marktein- oder -austritt bei aktuellen Marktpreisen erfordert. Ersetzen Sie Buy- und Sell-Order jeweils durch Buy-Limit- bzw. Sell-Limit-Order. Wenn Sie beispielsweise zum aktuellen Preis kaufen möchten, platzieren Sie eine Limit-Order mit einem maximalen Ausführungspreis, der geringfügig höher ist als der aktuelle Marktpreis. Das Gleiche gilt für den Verkauf. Platzieren Sie in diesem Fall Ihre Sell-Limit-Order mit einem etwas geringeren Preis als dem aktuellen Preis. Die Differenz zwischen dem Preis in einer Limit-Order und dem aktuellen Preis ist der maximale Slippage-Wert, den Sie akzeptieren.

Betrachten wir folgendes Beispiel. Nehmen wir an, wir kaufen ein hohes Volumen des EUR/USD-Terminkontrakts ED-3.15 bei 1,1356. Die aktuelle Liquidität ist gering. Dieser Moment wird absichtlich gewählt, um die Vorteile des Markteintritts unter Verwendung von Limit-Ordern zu demonstrieren. Der Zeitpunkt unseres Markteintritts fällt mit einem Preissprung zusammen, der im M1-Diagramm erkennbar ist:

Abb. 5 Markteintritt zum Zeitpunkt eines Preissprungs, ED-3.15

Es lässt sich erkennen, dass der Markteintrittspunkt absolut ungünstig ist. Analysieren wir das Tick-Diagramm für diesen Zeitpunkt:


Abb. 6 Tick-Diagramm und Ausführung der Limit-Order während des Liquiditätsdurchbruchs

Die Ausführung unserer Limit-Order wird durch große weiße Kreise (Ticks) dargestellt: . Die Ticks selbst werden durch runde blaue Punkte dargestellt. Wenn wir zum Marktpreis von 1,1356 kaufen, würde die Ausführung unserer Marktanfrage in mehreren Transaktionen stattfinden, angefangen beim Preis von 1,1356 bis zum Preis von 1,1398. Dies würde eine starke Slippage verursachen und unser durchschnittlicher Markteintrittspreis wäre wesentlicher schlechter als 1,1356. Je mehr Transaktionen für die Ausführung unserer Anfrage benötigt werden, desto schlechter fällt der durchschnittliche Eintrittspreis aus.

In unserem Fall wird diese große Preislücke durch geringe Liquidität verursacht, bei der Limit-Anfragen aus diversen Gründen verschwinden und der Preis chaotisch über einen großen Bereich pendelt. Doch eine Limit-Order verfügt über einen eingebauten Schutzmechanismus. Sie wird einfach nicht ausgeführt, wenn der aktuelle Preis über 1,1356 steigt. Zum Beispiel wurde unsere Limit-Order in sieben Transaktionen ausgeführt. Diese werden im Diagramm durch große weiße Kreise dargestellt. Zwischen diesen Transaktionen gab es andere Preise, doch diese waren alle schlechter als 1,1356. Deshalb wurden sie einfach ignoriert. Nach einer Weile stabilisierte sich der Preis und unsere Order wurde endlich vollständig ausgeführt.


1,4 Manuelle Festlegung einer Limit-Order für den Umgang mit der maximalen Slippage

Nachdem wir das Aktivierungsprinzip der Limit-Order betrachtet haben, ist es nun an der Zeit, unser Wissen praktisch unter realen Marktbedingungen einzusetzen. Nehmen wir an, unser Konto ist mit der Moskauer Börse verbunden. Platzieren wir eine Limit-Order bei einem Preis, der geringfügig schlechter ist als der aktuelle. Wählen wir außerdem den nächstgelegenen EUR/USD-Terminkontrakt (ED-6.15) als Arbeitssymbol. Rufen Sie das Fenster zum Öffnen einer Position auf und legen Sie eine Buy-Limit-Order mit einem etwas höheren Preis als dem aktuellen Ask fest:

Abb. 7 Manuelles Platzieren einer Limit-Order im Börsenmodus

Abb. 7 Manuelles Platzieren einer Limit-Order im Börsenmodus

Wie wir im Screenshot sehen können, liegt der aktuelle Ask-Preis bei 1,1242, unser Pending-Preis bei 1,1245. Die Differenz zwischen unserem Preis und dem besten Angebot beträgt 0,0003 Punkte (1,1245 - 1,1242 = 0,0003). Dieser Wert ist die maximale Slippage, die wir zu akzeptieren bereit sind. Im Börsenmodus entspricht eine solche Limit-Order dem Versand einer herkömmlichen Buy- oder Sell-Order mit maximaler Slippage (Deviation):

Abb. 8 Ausführung der Marktorder mit festgelegter Abweichung

Abb. 8 Ausführung der Marktorder mit festgelegter Abweichung

Da im Börsenmodus keine maximale Slippage verfügbar ist, kann die Abweichung nur durch die Festlegung einer Limit-Order gemäß Abbildung 7 festgelegt werden.


1,5 Einstellen der maximalen Slippage im Börsenmodus mithilfe des Expert Advisors

Lassen Sie uns eine Limit-Order mithilfe eines Programms festlegen. Dazu müssen wir ein simples Panel aus den folgenden Elementen schreiben:

  • BUY-Button – Kaufen mit einer BuyLimit-Order;
  • SELL-Button – Kaufen mit einer SellLimit-Order;
  • Ein Feld für die maximale Slippage (in Punkten) wird später hinzugefügt;
  • Das Kauf- oder Verkaufsvolumen wird ebenfalls in der nächsten Version des Panels hinzugefügt.

Der nachfolgende Screenshot zeigt die erste Version des Panels:

Abb. 9 Festlegen der maximalen Slippage im Panel DeviationPanel

Das Panel ist in Form einer CDevPanel-Klasse geschrieben. Sein Quellcode sieht so aus:

//+------------------------------------------------------------------+
//|                                                       Panel.mqh  |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Trade\Trade.mqh>
#define OP_BUY 0
#define OP_SELL 1
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CDevPanel
  {
private:
   CTrade            Trade;
   string            m_descr_dev;
   string            m_buy_button_name;
   string            m_sell_button_name;
   string            m_deviation_name;
   string            m_volume_name;
   string            m_bg_fon;
   int               m_deviation;
   void              OnObjClick(string sparam);
   void              OnEndEdit(string sparam);
   double            CalcCurrentPrice(int op_type);

public:
                     CDevPanel();
                    ~CDevPanel();
   void              OnChartEvent(const int id,
                                  const long &lparam,
                                  const double &dparam,
                                  const string &sparam);
  };
//+------------------------------------------------------------------+
//| CDevPanel class                                                  |
//+------------------------------------------------------------------+
CDevPanel::CDevPanel(): m_buy_button_name("buy_button"),
                        m_sell_button_name("sell_button"),
                        m_deviation_name("deviation"),
                        m_volume_name("volume"),
                        m_bg_fon("bg_fon"),
                        m_descr_dev("descr_dev"),
                        m_deviation(3)
  {
//--- background
   ObjectCreate(0,m_bg_fon,OBJ_RECTANGLE_LABEL,0,0,0);
   ObjectSetInteger(0,m_bg_fon,OBJPROP_YSIZE,80);
   ObjectSetInteger(0,m_bg_fon,OBJPROP_XSIZE,190);
   ObjectSetInteger(0,m_bg_fon,OBJPROP_BGCOLOR,clrWhiteSmoke);

//--- buy button
   ObjectCreate(0,m_buy_button_name,OBJ_BUTTON,0,0,0);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_XDISTANCE,100);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_YDISTANCE,50);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_XSIZE,80);
   ObjectSetInteger(0,m_buy_button_name,OBJPROP_BGCOLOR,clrAliceBlue);
   ObjectSetString(0,m_buy_button_name,OBJPROP_TEXT,"BUY");

//--- sell button
   ObjectCreate(0,m_sell_button_name,OBJ_BUTTON,0,0,0);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_XDISTANCE,10);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_YDISTANCE,50);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_XSIZE,80);
   ObjectSetInteger(0,m_sell_button_name,OBJPROP_BGCOLOR,clrPink);
   ObjectSetString(0,m_sell_button_name,OBJPROP_TEXT,"SELL");

//--- deviation
   ObjectCreate(0,m_deviation_name,OBJ_EDIT,0,0,0);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_XDISTANCE,120);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_YDISTANCE,20);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_XSIZE,60);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_BGCOLOR,clrWhite);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_COLOR,clrBlack);
   ObjectSetInteger(0,m_deviation_name,OBJPROP_ALIGN,ALIGN_RIGHT);
   ObjectSetString(0,m_deviation_name,OBJPROP_TEXT,(string)m_deviation);

//--- description
   ObjectCreate(0,m_descr_dev,OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_XDISTANCE,12);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_YDISTANCE,20);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_XSIZE,80);
   ObjectSetInteger(0,m_descr_dev,OBJPROP_BGCOLOR,clrWhite);
   ObjectSetString(0,m_descr_dev,OBJPROP_TEXT,"Deviation (pips):");
   ObjectSetInteger(0,m_descr_dev,OBJPROP_COLOR,clrBlack);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CDevPanel::~CDevPanel(void)
  {
   ObjectDelete(0,m_buy_button_name);
   ObjectDelete(0,m_sell_button_name);
   ObjectDelete(0,m_bg_fon);
   ObjectDelete(0,m_deviation_name);
   ObjectDelete(0,m_descr_dev);
  }
//+------------------------------------------------------------------+
//| Event function                                                   |
//+------------------------------------------------------------------+
void CDevPanel::OnChartEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam)
  {
   switch(id)
     {
      case CHARTEVENT_OBJECT_CLICK:
         OnObjClick(sparam);
         break;
      case CHARTEVENT_OBJECT_ENDEDIT:
         OnEndEdit(sparam);
     }
  }
//+------------------------------------------------------------------+
//| End edit detect                                                  |
//+------------------------------------------------------------------+
void CDevPanel::OnEndEdit(string sparam)
  {
   if(sparam != m_deviation_name)return;
   int value = (int)ObjectGetString(0, m_deviation_name, OBJPROP_TEXT);
   if(value <= 0)
      ObjectSetString(0,m_deviation_name,OBJPROP_TEXT,(string)m_deviation);
   else
      m_deviation=value;
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| End obj click                                                    |
//+------------------------------------------------------------------+
void CDevPanel::OnObjClick(string sparam)
  {
   if(sparam==m_buy_button_name)
      Trade.BuyLimit(1,CalcCurrentPrice(OP_BUY));
   if(sparam==m_sell_button_name)
      Trade.SellLimit(1,CalcCurrentPrice(OP_SELL));
   ObjectSetInteger(0,sparam,OBJPROP_STATE,false);
   Sleep(100);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Calc level price                                                 |
//+------------------------------------------------------------------+
double CDevPanel::CalcCurrentPrice(int op_type)
  {
   if(op_type==OP_BUY)
     {
      double ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      return ask + (m_deviation * Point());
     }
   else if(op_type==OP_SELL)
     {
      double bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
      return bid - (m_deviation * Point());
     }
   return 0.0;
  }
//+------------------------------------------------------------------+

Das Panel ermöglicht die Festlegung einer maximalen Slippage in Punkten auf Börsen mit Ausführung der Order auf dem Markt. Tatsächliche Marktein-/-austritte werden mithilfe von Limit-Ordern ausgeführt.

Das Panel funktioniert nur dann, wenn ein Broker die Marktausführung von Ordern bereitstellt. Andernfalls verursacht der Code einen Standardfehler mit einem falschen Preis bei der Eingabe des Preises einer Limit-Order:

2015.04.15 14:08:39.709 Trades  '58406864': failed buy limit 0.10 EURUSD at 1.05927 [Invalid price]


1,6 Buy-Stop-Limit- und Sell-Stop-Limit-Order als Alternative zu Buy-Stop- und Sell-Stop-Ordern

Limit-Order liefern einen bequemen und natürlichen Schutz vor Slippage. Doch manchmal müssen Pending Orders genutzt werden, die ausgelöst werden, wenn ein bestimmtes Niveau durchbrochen wird. Das naheliegendste Beispiel ist eine Stop-Loss-Order. Daneben müssen einige Strategien reagieren, wenn der Preis einen bestimmten Kanal verlässt. Sie benötigen ebenfalls Stop-Order zum Betreten des Marktes. Doch wie wir bereits wissen, unterliegen Stop-Order einer gewissen Slippage und sind nicht vor Liquiditätsproblemen geschützt. Zudem kann für sie keine maximale Slippage festgelegt werden.

In diesem Fall müssen wir Buy-Stop-Limit- und Sell-Stop-Limit-Order nutzen. Dabei handelt es sich um algorithmische Order für MetaTrader 5. Sie sind keine Marktorder, sondern werden stattdessen auf dem MetaTrader-Server umgesetzt. Sehen wir uns die offizielle Dokumentation an:

  • Buy Stop Limit – dieser Typ kombiniert die ersten zwei Typen, [Buy Limit und Buy Stop], und bildet eine Stop-Order zum Platzieren einer Buy-Limit-Order. Sobald der zukünftige Ask-Preis das in der Order festgelegte Stop-Niveau erreicht (Feld "Preis"), wird eine Buy-Limit-Order auf der im Feld "Stop-Limit-Preis" festgelegten Ebene platziert.
  • Sell Stop Limit – bei diesem Typen handelt es sich um eine Stop-Order zum Platzieren eines Sell Limit. Sobald der zukünftige Bid-Preis das in der Order festgelegte Stop-Niveau erreicht (Feld "Preis"), wird eine Sell-Limit-Order auf der im Feld "Stop-Limit-Preis" festgelegten Ebene platziert.

Die Dokumentation liefert auch die Abbildungen (Abb. 10), die das Arbeitsprinzip der Order in MetaTrader 5 illustrieren. Der gelbe Rahmen kennzeichnet die zwei Ordertypen, die für uns aktuell interessant sind:


Abb. 10 Typen von Ordern in MetaTrader 5

Also werden diese Limit-Order auf dem Markt platziert, wenn der Preis ein bestimmtes Stop-Niveau erreicht. Für eine Buy-Stop-Limit-Order wird eine Stop-Ebene oberhalb des aktuellen Ask-Preises platziert, für eine Sell-Stop-Limit-Order unterhalb des aktuellen Bid-Preises. Ein Limit-Orderpreis im Börsenmodus kann entweder oberhalb oder unterhalb des Stop-Preises dieser Order liegen. Dank dieser Besonderheit können wir spezielle Stop-Order mit kontrollierter Slippage konfigurieren. Die nachfolgende Abbildung zeigt, wie das funktioniert:

Abb. 11 Festlegen der maximalen Slippage durch die Platzierung einer Buy-Stop-Limit-Order

Wir können eine Buy-Stop-Limit-Order mit Limit Price über Stop Price platzieren. Sobald das Niveau von Stop Price erreicht wird, wird eine Buy-Limit-Order gesendet und unmittelbar ausgeführt, da der Limit Price schlechter ist als der aktuelle Stop Price. Die Differenz zwischen Stop Price und Limit Price bildet die maximale Slippage, die wir für unsere Order festlegen wollen. Sell-Stop-Limit-Order funktionieren auf die gleiche Weise, wobei der Limit Price in diesem Fall unterhalb des Stop Price liegen muss.

Wenden wir uns nun der Praxis zu und platzieren eine Buy-Stop-Limit-Order manuell.


1,7 Manuelle Festlegung von Buy-Stop-Limit- und Sell-Stop-Limit-Ordern anstelle von Stop Loss

Nehmen wir an, wir wollen unsere offene Position mithilfe einer Stop-Order schützen. Doch Märkte mit geringer Liquidität sind zu gefährlich und unberechenbar, um dort Stop- oder Marktorder zu nutzen. Eine Stop-Order (zum Beispiel Stop Loss) ist nicht gegen unbegrenzte Slippage geschützt. Also können große Preislücken oder -sprünge unser Konto vollständig ruinieren. Um das zu vermeiden, können wir Stop-Order durch Stop-Limits ersetzen.

Betrachten wir folgendes Beispiel. Nehmen wir an, wir haben eine lange Position auf Si-6.15. Der Stop Loss ist mit 56.960 festgelegt. Wir müssen die maximale Slippage von fünf Punkten festlegen, damit der Stop-Limit-Preis 56.960 - 5 = 56.955 Punkte beträgt:

Abb. 12 Platzieren einer SellStopLimit-Order als Stop-Level für eine lange Position

Abb. 12 Platzieren einer SellStopLimit-Order als Stop-Level für eine lange Position

Wie wir sehen, wird eine solche Konfiguration der Sell-Stop-Limit-Order im Börsenmodus ermöglicht. Wenn der aktuelle Preis 56.960 erreicht, wird eine Limit-Order für den Verkauf bei 56.955 platziert. Da der aktuelle Preis von 56.960 besser ist als der in der Limit-Order festgelegte, wird sie sofort bei 56.960 ausgeführt. Falls auf dieser Ebene nicht ausreichend Liquidität vorhanden ist, erfolgt die Ausführung bei den nachfolgenden Preisen bis 56.955. Die Limit-Order wird bei Preisen, die schlechter als 56.955 sind, nicht ausgeführt, wodurch eine maximale Slippage von fünf Punkten sichergestellt wird: 56 960 - 56 955 = 5.

Lassen Sie uns nun unsere kurze Position auf die gleiche Weise schützen. Um eine kurze Position nach Stop Loss zu schließen, müssen wir die entgegengesetzte Operation durchführen: Wir kaufen mithilfe einer Buy-Stop-Limit-Order. Nehmen wir an, der aktuelle Stop Loss für unsere kurze Position liegt bei 56.920. In diesem Fall müssen wir die folgende Konfiguration der Buy-Stop-Limit-Order nutzen, um die maximale Slippage von fünf Punkten sicherzustellen:

Abb. 13 Platzieren einer BuyStopLimit-Order als Stop-Level für eine kurze Position

Abb. 13 Platzieren einer BuyStopLimit-Order als Stop-Level für eine kurze Position

Diesmal übersteigt der Preis "Stop Limit price" den Preis "Price" um nur fünf Punkte und beträgt 56.925.


1,8 Ersetzen von Stop-Loss-Ebenen mit Buy-Stop-Limit- und Sell-Stop-Limit-Ordern im Expert Advisor

Kehren wir zu unserem Panel aus Abschnitt 1.5 zurück. Wir müssen es modifizieren, damit es das Platzieren von Schutzstopps mithilfe von Buy-Stop-Limit- und Sell-Stop-Limit-Ordern erlaubt. Fügen wir zu diesem Zweck ein Feld mit dem Namen Stop-Loss hinzu. Nun sieht unser Panel so aus:

Abb. 14 Platzieren eines Stop-Loss-Niveaus im DeviationPanel

Es gibt zwei wesentliche Änderungen am Code: Die Klasse CDevPanel verfügt nun über eine neue Methode zum Platzieren von Buy-Stop-Limit- und Sell-Stop-Limit-Ordern. Die Methode OnObjClick zum Öffnen einer neuen Position wurde modifiziert. Der Quellcode der Methoden sieht so aus:

//+------------------------------------------------------------------+
//| End obj click                                                    |
//+------------------------------------------------------------------+
void CDevPanel::OnObjClick(string sparam)
  {
   if(sparam==m_buy_button_name)
     {
      if(Trade.BuyLimit(1,CalcCurrentPrice(OP_BUY)))
         SendStopLoss(OP_BUY);
     }
   if(sparam==m_sell_button_name)
     {
      if(Trade.SellLimit(1,CalcCurrentPrice(OP_SELL)))
         SendStopLoss(OP_SELL);
     }
   ObjectSetInteger(0,sparam,OBJPROP_STATE,false);
   Sleep(100);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Send SL order                                                    |
//+------------------------------------------------------------------+
bool CDevPanel::SendStopLoss(int op_type)
  {
   if(op_type==OP_BUY)
     {
      double bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);
      if(m_sl_level>=0.0 && m_sl_level<bid)
        {
         MqlTradeRequest request={0};
         request.action = TRADE_ACTION_PENDING;
         request.symbol = Symbol();
         request.volume = 1.0;
         request.price=m_sl_level;
         request.stoplimit=m_sl_level -(m_deviation*Point());
         request.type=ORDER_TYPE_SELL_STOP_LIMIT;
         request.type_filling=ORDER_FILLING_RETURN;
         request.type_time=ORDER_TIME_DAY;
         MqlTradeResult result;
         bool res=OrderSend(request,result);
         if(!res)
            Print("Error set S/L. Reason: "+(string)GetLastError());
         return res;
        }
     }
   else if(op_type==OP_SELL)
     {
      double ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
      if(m_sl_level>=0.0 && m_sl_level>ask)
        {
         MqlTradeRequest request={0};
         request.action = TRADE_ACTION_PENDING;
         request.symbol = Symbol();
         request.volume = 1.0;
         request.price=m_sl_level;
         request.stoplimit=m_sl_level+(m_deviation*Point());
         request.type=ORDER_TYPE_BUY_STOP_LIMIT;
         request.type_filling=ORDER_FILLING_RETURN;
         request.type_time=ORDER_TIME_DAY;
         MqlTradeResult result;
         bool res=OrderSend(request,result);
         if(!res)
            Print("Error set S/L. Reason: "+(string)GetLastError());
         return res;
        }
      if(CharToStr(StringGetChar(data,strlen-1))=='.')
         StringSetChar(data,strlen-1,'');
     }
   return false;
  }

Neben diesen Methoden beinhaltet der Code der Panel-Klasse jetzt die Initialisierung und ein entsprechendes Feld für die Eingabe des Stop Loss. Wenn wir nun das Feld Stop Loss befüllen, bevor wir auf BUY oder SELL klicken, wird die neue Marktorder von einer speziellen Buy-Stop-Limit- oder Sell-Stop-Limit-Order (je nach Richtung der Position) zu ihrem Schutz begleitet.


KAPITEL 2 Analyse der Marktliquidität


2.1. Berechnung der Slippage vor dem Markteintritt

Ein charakteristisches Merkmal von Aktienbörsen ist der zentralisierte Handel. Somit können alle Limit-Order zum Kauf/Verkauf aus der Markttiefe beobachtet werden. Wenn wir zu den Definitionen aus dem Beitrag "Grundlagen der Preisbildung von Börsen anhand des Beispiels des Terminmarktes der Moskauer Börse" zurückkehren, sehen wir, dass Limit-Order innerhalb der Markttiefe die Marktliquidität (die Möglichkeit, bestimmte Volumina nahe des letzten Abschlusspreises Last zu kaufen und zu verkaufen) bereitstellen.

Je größer das Volumen ist, das wir verkaufen oder kaufen möchten, desto mehr Order aus der Markttiefe werden ausgelöst und erhöhen die Slippage, da wir von immer weiter entfernen Ebenen in Bezug auf den aktuellen Preis Liquiditätslieferanten gewinnen müssen. Im oben erwähnten Beitrag "Prinzipien der Preisbildung von Börsen" können Sie mehr darüber erfahren, wie Slippage funktioniert. Betrachten wir das folgende kurze Beispiel, um das Thema klarer zu machen.

Wir haben zu jedem bestimmten Zeitpunkt die Markttiefe, die Kauf-/Verkaufsvolumina beschreibt. Aktuell betrachten wir die Markttiefe auf dem USD/RUB-Terminkontrakt Si-6.15:


Abb. 15 Markttiefe des Terminkontrakts Si-6.15

Wenn wir 2 Kontrakte kaufen, sind wir nicht von Slippage betroffen, da der Abschluss zum besten Ask-Preis ausgeführt wird: 51 931. Doch wenn wir zum Beispiel 4 Kontrakte kaufen, wird sich unser Durchschnittspreis von 51.931 unterscheiden und besteht aus: (2*51.931+2*51.932)/4 = 51.931,5. Wir kaufen zwei Kontrakte für 51.931 und die restlichen zwei für 51.932. 51.931,5 ist der gewichtete mittlere Markteintrittspreis. Die Differenz zwischen diesem Preis und dem besten Ask-Preis bildet den Wert unserer Slippage.

Nun können wir die Tabelle der Liquidität erstellen, die den Slippage-Wert abhängig vom Volumen unseres Abschlusses definiert. Beim Volumen von 1 oder 2 Kontrakten wird unser Abschluss zum besten Ask-Preis (51.931) ohne Slippage ausgeführt. Bei 4 Kontrakten beträgt die Slippage 0,5 Punkte (51.931,5 - 51.931,0). Die Gleichung ist einfach: Der beste Ask- oder Bid-Preis (je nach Richtung des Abschlusses) wird vom gewichteten mittleren Markteintrittspreis abgezogen.

Nachfolgend sehen Sie die Liquiditätstabelle:

Volumen Kurs Volumen des
Abschlusses
Gewichteter mittlerer
Markteintrittspreis
Slippage
2  51 938 25 51 934,5. 3,5
9  51 936  23 51 934,2 3,2
3  51 935  14 51 933,0 2,0
7  51 933  11 51 932,5 1,5
2  51 932  4 51 931,5 0,5
2  51 931  2 51 931,0 0,0

Tabelle 1 Berechnung des gewichteten mittleren Eintrittspreises und der jeweiligen Slippage

Diese Tabelle muss von unten nach oben gelesen werden, genauso wie der Ask-Preis der Markttiefe. Wie wir sehen können, weist das Abschlussvolumen von zwei Kontrakten keine Slippage auf. Das Abschlussvolumen von vier Kontrakten hat eine Slippage von 0,5 Punkten. Das Abschlussvolumen von 25 Kontrakten hat eine Slippage von 3,5 Punkten und sein gewichteter mittlerer Preis beträgt 51934,5.

Der zentralisierte Markt und die Markttiefe ermöglichen es uns, die folgende Schlussfolgerung zu ziehen:

Dank der Kenntnis des Zustands der Markttiefe können wir die potenzielle Slippage berechnen, bevor ein Abschluss durchgeführt wird.

Somit können wir unser Risiko kontrollieren. Unabhängig davon, ob wir manuell oder mithilfe von Handelsrobotern handeln, können wir die Tiefe des Marktes definieren, bevor wir ihn betreten. In diesem Fall können wir den Händler mit einem Taucher vergleichen. Bevor er ins Wasser springt, muss der Taucher die Tiefe des Beckens kennen. Je größer der Taucher ist, desto tiefer muss das Becken sein. Genauso verhält es sich beim Handel: Je größer das Abschlussvolumen ist, desto liquider muss der Markt sein. Natürlich kann sich die Markttiefe ändern, kurz bevor wir den Markt betreten. Doch auch eine etwas veraltete Berechnung ist noch akkurat genug, um einen Abschluss durchzuführen.


2,2 Berechnung der potenziellen Slippage in Echtzeit

Setzen wir die Theorie nun in die Praxis um. Es ist unmöglich, den Wert der potenziellen Slippage manuell zu berechnen, da die Markttiefe sich zu schnell ändert und die Berechnung selbst ziemlich mühselig ist. Also müssen wir sie automatisieren. Um die Berechnung zu erleichtern, setzen wir die spezielle Klasse CMarketBook für die Arbeit mit der Markttiefe um. Die Entwicklung einer solchen Klasse ist eine schwierige Aufgabe, die ihren eigenen Beitrag verdient. Die Prinzipien ihrer Arbeit müssen hier nicht unbedingt beschrieben werden. Stattdessen verwenden wir eine ihrer Methoden: GetDeviationByVol. Sehen wir uns an, wie sie funktioniert:

//+------------------------------------------------------------------+
//| Get deviation value by volume. Retun -1.0 if deviation is        |
//| infinity (insufficient liquidity)                                |
//+------------------------------------------------------------------+
double CMarketBook::GetDeviationByVol(long vol,ENUM_MBOOK_SIDE side)
  {
   int best_ask = InfoGetInteger(MBOOK_BEST_ASK_INDEX);
   int last_ask = InfoGetInteger(MBOOK_LAST_ASK_INDEX);
   int best_bid = InfoGetInteger(MBOOK_BEST_BID_INDEX);
   int last_bid = InfoGetInteger(MBOOK_LAST_BID_INDEX);
   double avrg_price=0.0;
   long volume_exe=vol;
   if(side==MBOOK_ASK)
     {
      for(int i=best_ask; i>=last_ask; i--)
        {
         long currVol=MarketBook[i].volume<volume_exe ?
                      MarketBook[i].volume : volume_exe;
         avrg_price += currVol * MarketBook[i].price;
         volume_exe -= MarketBook[i].volume;
         if(volume_exe<=0)break;
        }
     }
   else
     {
      for(int i=best_bid; i<=last_bid; i++)
        {
         long currVol=MarketBook[i].volume<volume_exe ?
                      MarketBook[i].volume : volume_exe;
         avrg_price += currVol * MarketBook[i].price;
         volume_exe -= MarketBook[i].volume;
         if(volume_exe<=0)break;
        }
     }
   if(volume_exe>0)
      return -1.0;
   avrg_price/=(double)vol;
   double deviation=0.0;
   if(side==MBOOK_ASK)
      deviation=avrg_price-MarketBook[best_ask].price;
   else
      deviation=MarketBook[best_bid].price-avrg_price;
   return deviation;
  }

Wenn die Methode aufgerufen wird, verweist sie auf die Markttiefe. Sie geht die Markttiefe durch, beginnend beim besten Preis, und berechnet das dort verfügbare Volumen. Sobald das verfügbare Volumen dem erforderlichen entspricht oder dieses übersteigt, bricht die Methode die Suche ab und berechnet einen gewichteten mittleren Preis gemäß einem vorher bestimmten Volumen. Die Differenz zwischen dem berechneten gewichteten mittleren Preis und dem besten Bid- oder Ask-Preis bildet den Wert unserer potenziellen Slippage.

Wenn die Liquidität der Markttiefe aus irgendeinem Grund nicht für das festgelegte Volumen ausreicht, gibt die Methode -1.0 aus, was bedeutet, dass die potenzielle Slippage nicht berechnet werden kann.

Da wir nun über die Berechnungsmethode für die potenzielle Slippage verfügen, müssen wir die erhaltenen Ergebnisse auch visualisieren. Natürlich hängt der Wert der Slippage eng mit dem auf dem Markt gekauften oder verkauften Volumen zusammen. Je höher das Volumen, desto größer die Slippage. Deshalb müssen wir eine neue Zeile und ein Eingabefeld mit dem Namen Volume zu unserem Panel hinzufügen:

Abb. 16 Das Panel mit dem Feld Volume

Nun kann unser Panel ein beliebiges Volumen kaufen und verkaufen. Wenn wir beispielsweise 5 Kontrakte zum Marktpreis kaufen wollen, müssen wir einfach 5 in das Feld Volume eingeben und auf BUY klicken. Doch das ist nicht die einzige Neuerung. Wie bereits erwähnt, können wir die Slippage dank der Methode GetDeviationVol beim Markteintritt kontrollieren.

Für eine bessere Sichtbarkeit lassen wir den berechneten Wert direkt auf den Buttons BUY und SELL anzeigen. Wir legen unsere Slippage in Punkten fest. Dieser Wert wird bei jeder Änderung der Markttiefe neu berechnet. Wenn die Liquidität steigt, sinkt die Slippage und umgekehrt. Wenn wir nur einen Kontrakt kaufen oder verkaufen wollen, gibt es überhaupt keine Slippage, da das Volumen von 1 niemals über dem besten Bid-/Ask-Volumen liegt.

Ich empfehle, dass Sie sich die Arbeit des aktualisierten Panels in Echtzeit ansehen. Das nachfolgende Video zeigt die Berechnung der potenziellen Slippage für den Terminkontrakt RTS-6.15 in Echtzeit:

Am Anfang wird ein Kontrakt in das Feld Volume eingegeben. Wie erwartet, zeigen die Buttons BUY und SELL 0 an. Das bedeutet, dass unser Markteintritt keine Slippage erzeugt. Nach der Erhöhung des Volumens auf 100 Kontrakte steigt die durchschnittliche Slippage für Käufe und Verkäufe auf 10-20 Punkte. Wenn das Volumen auf 500 Kontrakte erhöht wird, steigt die durchschnittliche Slippage auf 60-80 Punkte. Nachdem wir letztendlich das Volumen auf 1.500 Kontrakte erhöht haben, stoßen wir auf ungenügende Liquidität, die durch den Wert -1.0 auf dem Button BUY angezeigt wird (Slippage kann nicht definiert werden). Die Liquidität der Nachfrage reicht immer noch aus, obwohl der Verkauf einer so hohen Menge von Kontrakten eine Slippage von 100-130 Punkten verursachen würde.

Die Klasse für die Arbeit mit der Markttiefe sowie die finale Version des DeviationPanel finden Sie in den unten angehängten Quellcodes.


2,3 Verwendung des Spread-Indikators SpreadRecord als Filter für den Markteintritt

Die aktuelle Liquidität vor dem Markteintritt zu analysieren, ist eine hilfreiche und vernünftige Angewohnheit. Ein gut programmierter Handelsroboter, der komplexe Berechnungen für Sie übernimmt, kann Sie vor gefährlicher Slippage bewahren. Doch das reicht nicht immer.

Ein weiteres Problem, um das sich Händler kümmern müssen, ist eine zuverlässige Bestimmung der Breite des Spreads. Ein Spread ist die Differenz zwischen den besten Bid- und Ask-Preisen. Bei einem Spread handelt es sich weitestgehend um einen relativen Parameter, da Abschlüsse mit großen Volumina größtenteils durch die allgemeine Liquidität der Markttiefe beeinflusst werden, anstatt durch die Breite des Spreads. Allerdings haben Händler für gewöhnlich keinen Zugriff auf die Historie der Markttiefe. Deshalb ist es äußerst schwierig, die vergangene Liquidität eines Handelskontrakts zu beurteilen. Andererseits verhält sich ein Spread umgekehrt proportional zur Liquidität eines Symbols. Je enger der Spread, desto höher die Liquidität und umgekehrt.

Mit dieser Eigenschaft im Hinterkopf können wir einen Spread-Indikator entwickeln, der vergangene Spread-Volumina anzeigt. Dieser Indikator ist für den Handel äußerst nützlich, da er es uns ermöglicht, vergangene Liquiditätswerte und die Breite des Spreads eines Symbols zu bewerten. Wenn wir den durchschnittlichen Spread kennen, können wir unseren Handel bei einer starken Veränderung der Liquidität eingrenzen, wenn der Spread sich merklich ausdehnt.

Lassen Sie uns einen solchen Indikator erstellen. Er wird seine Werte in Form von Balken im unteren Diagrammfenster anzeigen. Das durchschnittliche Spread-Niveau wird als grüner Punkt beim jeweiligen Niveau eines Balkens angezeigt. Der Indikator berechnet die folgenden Spread-Werte:

  • Wert des Spreads bei der Öffnung des Balkens;
  • maximales Spread-Niveau während der Laufzeit des Balkens;
  • minimales Spread-Niveau während der Laufzeit des Balkens;
  • Wert des Spreads bei der Schließung des Balkens;
  • durchschnittlicher Wert des Spreads während der Laufzeit des Balkens.

Der Indikator speichert nicht die Werte des Spreads und zeichnet den Spread ab dem letzten Balken nach dem Neuladen des Terminals. Nachfolgend sehen Sie den Quellcode des Indikators:

//+------------------------------------------------------------------+
//|                                                Spread Record.mq4 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "https://www.mql5.com/ru/users/c-4"
#property version   "1.00"
#property description "Recording spread and show it."
#property indicator_separate_window
#property indicator_buffers 5
#property indicator_plots   5
#property indicator_type1   DRAW_BARS
#property indicator_type2   DRAW_ARROW
#property indicator_color1   clrBlack
#property indicator_color2   clrBlack
double spread_open[];
double spread_high[];
double spread_low[];
double spread_close[];
double spread_avrg[];
int elements;
double avrg_current;
int count;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,spread_open,INDICATOR_DATA);
   SetIndexBuffer(1,spread_high,INDICATOR_DATA);
   SetIndexBuffer(2,spread_low,INDICATOR_DATA);
   SetIndexBuffer(3,spread_close,INDICATOR_DATA);
   SetIndexBuffer(4,spread_avrg,INDICATOR_DATA);
   IndicatorSetInteger(INDICATOR_DIGITS,1);
   PlotIndexSetInteger(1,PLOT_ARROW,0x9f);
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,clrRed);
   PlotIndexSetInteger(1,PLOT_LINE_COLOR,clrGreen);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   printf("DEINIT");
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   if(prev_calculated==0)
     {
      printf("INITIALIZE INDICATORS "+TimeToString(TimeCurrent()));
      double init_value=EMPTY_VALUE;
      ArrayInitialize(spread_high,init_value);
      ArrayInitialize(spread_low,init_value);
      ArrayInitialize(spread_open,init_value);
      ArrayInitialize(spread_close,init_value);
      ArrayInitialize(spread_avrg,init_value);
      elements=ArraySize(spread_high);
      InitNewBar(elements-1);
     }
//--- new bar initialization
   for(; elements<ArraySize(spread_high); elements++)
      InitNewBar(elements);
   double d=GetSpread();
   for(int i=rates_total-1; i<rates_total; i++)
     {
      if(d>spread_high[i])
         spread_high[i]=d;
      if(d<spread_low[i])
         spread_low[i]= d;
      spread_close[i] = d;
      avrg_current+=d;
      count++;
      spread_avrg[i]=avrg_current/count;
     }
//--- return value of prev_calculated for next call
   return(rates_total-1);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double GetSpread()
  {
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   return NormalizeDouble((ask-bid)/Point(), 0);
  }
//+------------------------------------------------------------------+
//| Init new bar                                                     |
//+------------------------------------------------------------------+
void InitNewBar(int index)
  {
   spread_open[index] = GetSpread();
   spread_high[index] = 0.0;
   spread_low[index]=DBL_MAX;
   avrg_current=0.0;
   count=0;
  }

Versuchen wir, diesen Indikator auf dem Minutendiagramm von Si-6.15 auszuführen. Kurz nach dem Start zeigt er die folgenden Ergebnisse:

Abb. 17 Indikator SpreadRecord auf dem Minutendiagramm von Si-6.15

Wir sehen, dass der Spread für Si-6.15 während des analysierten Zeitraums zwischen 1 und 21 Punkten schwankte. Bei jeder Minute gab es mindestens einen Moment, in dem der Spread dem Mindestwert von 1 Punkt entsprach. Der durchschnittliche Wert liegt bei 3 Punkten. Wie bereits erwähnt, wird er im Indikatorfenster als grüner Punkt angezeigt.


2,4 Manuelle und automatische Einschränkung des Handels bei starken Spread-Erweiterungen

Nun müssen wir lernen, wie wir den Indikator nutzen können, um unsere Risiken zu kontrollieren. Das Einfachste, was wir tun können, ist, unsere Handelsaktivitäten einzuschränken, wenn die aktuellen Indikatorwerte zu hoch sind. Im ausgewählten Zeitraum lagen die Indikatorwerte größtenteils innerhalb eines Bereichs von 1-9 Punkten. Dieser Bereich kann als "grün" bezeichnet werden. Der Handel ist hier aktiviert. Wenn der Spread über 9 Punkte steigt, bewegen wir uns in die rote Zone, in der der Handel deaktiviert ist. Dies lässt sich folgendermaßen darstellen:


Abb. 18. Durch den Indikator bestimmte Zonen des aktivierten und deaktivierten Handels

Zusätzlich zur manuellen Einschränkung des Handels müssen wir unserem Expert Advisor auch beibringen, die Indikatorwerte zu akzeptieren und dessen Handelstätigkeiten einzuschränken, wenn der aktuelle Spread die festgelegten Grenzen überschreitet. Das können Sie bewerkstelligen, indem Sie den Indikator mithilfe der Funktion iCustom aus dem EA aufrufen. Mit dieser Funktion können sie alle benutzerdefinierten Indikatoren direkt aus dem EA aufrufen, der ihre Werte erhält. Nachfolgend sehen Sie die Vorlage für den EA, der den Spread durch den Indikator kontrolliert:

//+------------------------------------------------------------------+
//|                                          SpreadRecordControl.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
#property version   "1.00"
#define OPEN  0
#define HIGH  1
#define LOW   2
#define CLOSE 3
#define AVRG  4

input int MaxSpread=9;

int h_spread_record=INVALID_HANDLE;       // Handle of SpreadRecord indicator
bool print_disable = false;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   h_spread_record=iCustom(Symbol(),Period(),"Spread Record");
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinit function                                           |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(h_spread_record);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(IsTradeDisable(MaxSpread))return;
   //
   // TRADE LOGIC...
   //
  }
//+------------------------------------------------------------------+
//| Return true if trade disable, otherwise return false             |
//+------------------------------------------------------------------+
bool IsTradeDisable(int max_spread)
  {
   if(h_spread_record==INVALID_HANDLE)
      return false;
   double close[];
   if(CopyBuffer(h_spread_record, CLOSE, 0, 1, close) < 1)return false;
   if(close[0]>MaxSpread)
     {
      if(!print_disable)
         printf("trade disable");
      print_disable=true;
      return true;
     }
   if(print_disable)
      printf("trade enable");
   print_disable=false;
   return false;
  }

Die Funktion IsTradeDisable definiert, ob der Handel aktiviert ist oder nicht. Sie gibt true aus, wenn der Spread zu hoch ist und der Handel deaktiviert werden sollte. Wenn der Spread normal ist, gibt die Funktion false aus. Die Funktion basiert auf dem Aufruf des Indikators SpreadRecord durch Kopieren seines aktuellen Werts mithilfe der Funktion CopyBuffer. Der EA verfügt über den Parameter MaxSpread, der dem Schwellenwert entspricht. Wenn der Wert überschritten wird, blockiert der EA seine Handelsaktivitäten. Wenn der Spread wieder unter den festgelegten Grenzwert fällt, nimmt der EA seine Arbeit wieder auf. Die Funktion IsTradeDisable signalisiert den Übergang von einem Zustand zum anderen durch die entsprechende Meldung: "trade enable" und "trade disable":

2015.05.27 16:57:08.238 SpreadRecordControl (Si-6.15,H1)        trade enable
2015.05.27 16:57:08.218 SpreadRecordControl (Si-6.15,H1)        trade disable
2015.05.27 16:56:49.411 SpreadRecordControl (Si-6.15,H1)        trade enable
2015.05.27 16:56:49.401 SpreadRecordControl (Si-6.15,H1)        trade disable
2015.05.27 16:56:36.478 SpreadRecordControl (Si-6.15,H1)        trade enable
2015.05.27 16:56:36.452 SpreadRecordControl (Si-6.15,H1)        trade disable

Sie können diesen Prototypen des EA für Ihre Handelssysteme nutzen und somit den Markteintritt bei geringer Liquidität und großer Slippage vermeiden.

Die Quellcodes des Expert Advisors und des Indikators SpreadRecord wurden an den Beitrag angehängt.


KAPITEL 3 Sichere Modi für den Handel und das Testen von Expert Advisors


3,1 Nutzung des "Sleep-Modus" als Alternative zur Kontrolle Tick für Tick

Wie bereits in Abschnitt 1.1 "Diskretheit des Preisflusses. Preislücken" erwähnt, lassen sich Marktgebote mit einem kontinuierlichen Fluss von Preisen vergleichen. Wenn sich also der Preis einer Aktie von 10 $ zu 15 $ ändert, bedeutet das, dass es Zeitpunkte gab, zu denen der Preis bei 11, 12, 13 und 14 Dollar lag. Allerdings haben wir bereits herausgefunden, dass das nicht immer der Fall ist.

Preise können sich oft scharf verschieben, während unsere Handelsmethoden für gewöhnlich auf der Annahme gründen, dass die Gebote sich schrittweise und gleichförmig ändern. Wenn wir einen Stop Loss festlegen, nehmen wir an, dass unsere Position beim Stop-Loss-Niveau geschlossen wird, bevor sie einen vernichtenden Verlust erleidet. Allerdings bildet die Basis jeder Stop-Order die Zustimmung, bei jedem verfügbaren Preis zu kaufen oder verkaufen, wenn dieser ein bestimmtes Niveau überschreitet. Bei diskreten Preisen wird aus einer Stop-Order ein potenzieller Verlustbringer. Wenn der aktuelle Preis um mehrere Male schlechter ist als der in unserer Stop-Order festgelegte, wird der Stop Loss dennoch ausgeführt und beschert uns einen viel höheren Verlust.

Wenn andererseits ein Expert Advisor die Marktsituation bei jedem Tick prüft, riskiert er auch, eine Position bei einem sehr ungünstigen Preis zu schließen: Bei geringer Liquidität wird der letzte Abschluss, der diesen Tick erzeugt, möglicherweise zum Teil ausgeführt und erreicht dabei die unglaublichsten Preise und erzeugt somit einen Preissprung.

Anstatt jeden Markt-Tick zu verfolgen, ist es deshalb hilfreicher, eine Strategie bis zu einem gewissen Grad zu "desensibilisieren", sodass die Handelslogik eines EAs einmal in einem bestimmten Zeitraum (z. B. einmal pro Minute) und nicht bei jedem Tick aufgerufen wird. Eine Verwendung von Stop-Ordern kommt ebenfalls nicht in Frage. Stattdessen wäre es nützlicher, algorithmische (virtuelle) Stopps zu nutzen, deren Aktivierungsbedingungen einmal in einem bestimmten Zeitraum überprüft werden.

Es kann der Eindruck entstehen, dass eine solche Desensibilisierung der Handelslogik die Handelsergebnisse deutlich verzerren würde, doch das ist nicht der Fall. Natürlich kann sich der Preis innerhalb einer Minute weit von einem potenziellen Marktaus- oder -eintrittspreis entfernen, doch es wird auch günstigere Bedingungen geben, in denen Abschlüsse von den Preisumkehrpunkten weg ausgeführt werden.

Betrachten wir einen realen Marktfall, der am 28. Mai für den RUB/USD-Terminkontrakt SI-6.15 beobachtet wurde. Um 10:03 (Moskauer Zeit) trat ein bedeutender Preissprung auf. Nehmen wir an, wir hatten zu diesem Zeitpunkt eine lange Position bei 53.040 mit Stop Loss bei 52.740 (300 Punkte). In diesem Fall wäre unser Stop Loss bei einem Preis ausgelöst worden, der viel niedriger ist als der im Stop Loss festgelegte.

Die Praxis zeigt, dass Stop-Niveaus für gewöhnlich bei Preisen ausgelöst werden, die nahe an den schlechtesten Preisen während eines Preissprungs liegen. In diesem Fall wäre das 52.493 und hätte somit einen Verlust von 53.040 - 52 493 = 547 Rubel pro Kontrakt bedeutet (anstatt der in unserem Stop-Level festgelegten 300 Rubel). Dieser Fall wird nachfolgend in Diagramm A illustriert. Hätten wir unseren Stop Loss einmal pro Minute geprüft, hätte unsere Strategie den Preissprung ignoriert, der Stop Loss wäre nicht ausgelöst worden und letztendlich hätten wir unseren Abschluss mit Gewinn ausgeführt (Diagramm B):

Abb. 19 Unterschiedliches Verhalten der Strategie abhängig davon, ob eine reale oder virtuelle Stop-Order verwendet wird

Abb. 19 Unterschiedliches Verhalten der Strategie abhängig davon, ob eine reale oder virtuelle Stop-Order verwendet wird

Der hier gezeigte Preissprung ist vergleichsweise klein. Doch manchmal können Preissprünge sogar die Preisgrenze eines Terminkontrakts erreichen. Preisgrenzen haben für gewöhnlich einen Abstand von 5 % vom aktuellen Preis. Wenn wir also einen Hebel von 1:1 nutzen, riskieren wir einen Verlust von 5 % unserer Einlage während der Ausführung eines Stop-Levels. Wenn wir einen Hebel von 1:10 nutzen, umfassen die entstehenden Verluste 50 % unserer Einlage!


3,2 Beispiel-Expert-Advisor auf Basis von gleitenden Mittelwerten und die Prüfung seiner Handelslogik einmal pro Periode

Der Beispiel-Expert-Advisor, der auf dem Schnittpunkt zweier gleitender Mittelwerte betrieben wird, stellt ein gutes Beispiel dafür dar, wie Sie einen EA so einrichten können, dass er die Marktbedingungen einmal pro Periode prüft. Der letzte Wert eines gleitenden Mittelwerts (Moving Average, MA) verändert sich ständig und folgt dem sich ständig ändernden Schließungspreis (Close) des letzten Balkens.

Die klassische Strategie auf Basis von zwei MAs ist vielen Händlern gut bekannt. Der EA kauft, wenn ein schneller MA einen langsamen von unten überkreuzt, und verkauft, wenn ein schneller MA einen langsamen von oben überkreuzt. Die folgende Abbildung illustriert Markteintrittssignale für lange und kurze Positionen für diese Strategie:

Abb. 20 Markteintrittssignale für lange und kurze Positionen für die MovingAverage-Strategie

Abb. 20 Markteintrittssignale für lange und kurze Positionen für die MovingAverage-Strategie

Wie bereits erwähnt, ändern sich die MAs des letzten Balkens ständig. In diesem Fall kann ein schneller MA einen langsamen innerhalb eines einzigen Balkens mehrfach überkreuzen und einen EA zur Durchführung etlicher Umkehrungen zwingen, während der Preis fast stillsteht. Wir wissen auch schon, dass es besser ist, die Marktbedingungen nicht bei jedem neu eingegangenen Tick auf Terminmärkten zu prüfen. Also sollten wir den EA so einrichten, dass er die Handelsbedingungen einmal pro Minute prüft. In diesem Fall prüft der EA den vorherigen (bereits vollständigen) Balken anstelle des aktuellen, sodass die Neuzeichnung des MA beim letzten Balken sich nicht auf das Verhalten des EAs auswirkt.

Nachfolgend sehen Sie den Code des MovingAverage-EAs:

//+------------------------------------------------------------------+
//|                                                MovingAverage.mq5 |
//|                                 Copyright 2015, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Trade\Trade.mqh>

input int FastMAPeriod = 10;     // Fast MA period
input int SlowMAPeriod = 20;     // Slow MA period
input double Volume = 1.0;       // Volume for Trade
int FastMA = INVALID_HANDLE;     // Handle of fast MA indicator.
int SlowMA = INVALID_HANDLE;     // Handle of slow MA indicator.
datetime TimeLastBar;
CTrade Trade;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   FastMA = iMA(Symbol(), Period(), FastMAPeriod, MODE_SMA, 1, PRICE_CLOSE);
   SlowMA = iMA(Symbol(), Period(), SlowMAPeriod, MODE_SMA, 1, PRICE_CLOSE);
   if(FastMA==POINTER_INVALID || SlowMA==POINTER_INVALID)
     {
      printf("handle of indicator has not been created");
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   IndicatorRelease(FastMA);
   IndicatorRelease(SlowMA);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(!NewBarDetect())return;
   if(CrossOver())
      Trade.Buy(GetVolume());
   else if(CrossUnder())
      Trade.Sell(GetVolume());
  }
//+------------------------------------------------------------------+
//| Return true if fast ma cross slow ma over. Otherwise return      |
//| false.                                                           |
//+------------------------------------------------------------------+
bool CrossOver()
  {
   double fast_ma[];
   double slow_ma[];
   if(CopyBuffer(FastMA, 0, 1, 2, fast_ma) < 1)return false;
   if(CopyBuffer(SlowMA, 0, 1, 2, slow_ma) < 1)return false;
   bool is_over=fast_ma[1]>slow_ma[1] && fast_ma[0]<slow_ma[0];
   return is_over;
  }
//+------------------------------------------------------------------+
//| Return true if fast ma cross slow ma under. Otherwise return     |
//| false.                                                           |
//+------------------------------------------------------------------+
bool CrossUnder()
  {
   double fast_ma[];
   double slow_ma[];
   if(CopyBuffer(FastMA, 0, 1, 2, fast_ma) < 1)return false;
   if(CopyBuffer(SlowMA, 0, 1, 2, slow_ma) < 1)return false;
   bool is_under=fast_ma[0]>slow_ma[0] && fast_ma[1]<slow_ma[1];
   return is_under;
  }
//+------------------------------------------------------------------+
//| Return count volume for trade/                                   |
//+------------------------------------------------------------------+
double GetVolume()
  {
   if(PositionSelect(Symbol()))return Volume*2.0;
   return Volume;
  }
//+------------------------------------------------------------------+
//| Return true if new bar detect, otherwise return false.           |
//+------------------------------------------------------------------+
bool NewBarDetect()
  {
   datetime times[];
   if(CopyTime(Symbol(),Period(),0,1,times)<1)
      return false;
   if(times[0] == TimeLastBar)return false;
   TimeLastBar = times[0];
   return true;
  }
//+------------------------------------------------------------------+

 Die wichtigste Besonderheit des EAs bildet die Bedingung für die Prüfung des Eingangs eines neuen Balkens:

void OnTick()
{
   if(!NewBarDetect())return;
   ...
}

Die aktuelle Version des EAs verfügt über keinen Stop Loss. Würde er allerdings verwendet, dann würde nach der Funktion der Überprüfung des neuen Balkens dennoch eine Prüfung auf den Austritt aus einer Position nach Stop Loss stattfinden, sodass ein Stop Loss nur nach dem Eingang eines neuen Balkens ausgelöst werden kann.

Dies ermöglicht uns die Analyse der Marktbedingungen nur dann, wenn ein neuer Balken geöffnet wird, wodurch potenzielle Preissprünge verhindert werden. Natürlich kann ein Preissprung auch gleich beim Eingang eines neuen Balkens stattfinden, doch das ist hunderte Male weniger wahrscheinlich als bei der Überprüfung der Marktbedingungen bei jedem Tick.


3,3 Nutzung des Modus mit abgeschlossenen Balken als Alternative zum Testen von Expert Advisors Tick für Tick

Betrachten wir zu guter Letzt einen der interessantesten Testmodi für EAs und Indikatoren, der im Strategietester von MetaTrader 5 zur Verfügung steht. Dieser Modus heißt "Open prices only" (nur Öffnungspreise). Starten Sie den Strategietester (View --> Strategy Tester) und wählen Sie ihn im Abschnitt Execution (Ausführung) des Testerfensters.

Abb. 21 Auswahl des Modus "Open prices only"

Abb. 21 Auswahl des Modus "Open prices only"

Händler unterschätzen diesen Modus oft und sind der Meinung, er sei nicht genau genug. Außerdem können nur wenige EAs diesen Modus wirksam nutzen. Doch einige wenige wissen, dass es sich dabei tatsächlich um den genauesten und schnellsten Testmodus handelt, insbesondere im Vergleich zu "Every tick" (Jeder Tick).

Die hohe Genauigkeit wird durch die ausschließliche Nutzung von Öffnungspreisen erreicht. Alle Preise des neuen Balkens werden erst dann zugänglich, wenn er vollständig ist und somit zum vorherigen Balken in der Gebotshistorie wird.

Im Vergleich dazu wird im Modus "Every tick" jeder Balken speziell geformt und erhält Informationen vom kleinsten verfügbaren Timeframe mithilfe seines Tick-Indikators. Da die Tick-Historie in MetaTrader 5 nicht gespeichert wird, kann der Generator keine Preislücken innerhalb eines Minutenbalkens simulieren. Somit kann ein "Heiliger Gral" entwickelt werden, der zu ausgezeichneten Ergebnissen im Tester führt, auf dem realen Markt allerdings nicht funktioniert.

Der Testmodus Tick für Tick ist die größte Gefahr für Strategien, die auf dem Durchbruch von Ebenen basieren. Zudem können Pending Orders auch die tatsächlichen Ergebnisse verzerren. Betrachten wir eine Strategie, die eine Pending Order des Typen Buy Stop platziert und auf eine starke Aufwärtsbewegung wartet. Am 25. Mai 2015 um 19:00 Uhr (gleich nach dem abendlichen Clearing) bewegte sich der Terminkontrakt SBRF-6.15 innerhalb einer Minute von 7.473 zu 7.530 Rubeln. Hätten wir eine Pending Order bei 7.485, wäre sie bei ihrem festgelegten Preis im Strategietester ausgelöst worden und hätte nach der Schließung der Position einige Balken später einen Gewinn mit sich gebracht:

Abb. 22 Aktivierung einer Pending Order

Doch in der Realität kann das ganz anders aussehen. Wir wissen nichts über die Preise innerhalb des Minutenbalkens. In anderen Worten: Die Order kann genauso gut bei einem viel schlechteren Preis ausgeführt werden. Das nachfolgende Video zeigt, wie die Order im Modus "Every tick" ausgeführt wird:

 

Wie wir sehen können, hat der Strategietester keine Probleme bei der Verarbeitung des von uns festgelegten Preises. Doch sehen wir uns auch das Tick-Diagramm dieser Minutenkerze an:

Abb. 23 Tick-Diagramm der Minutenkerze

Der Preis hat innerhalb dieser Minute drastische Änderungen durchgemacht. Das Tick-Diagramm weist große Preislücken und scharfe Bewegungen auf. Also wäre unsere Stop-Order unter realen Marktbedingungen kaum beim gewünschten Preis ausgeführt worden. Der tatsächliche Ausführungspreis hätte sich höchstwahrscheinlich zwischen 7.510 und 7.520 befunden.

Eine Analyse jedes Ticks und die Verwendung von Marktpreisen anstelle von Pending Orders hätte keinen Unterschied gemacht. Da der Tick-Generator des Strategietesters Ticks nacheinander erzeugt, wäre unsere Order ausgelöst worden, sobald der Ask-Preis die von uns festgelegte Ebene berührt hätte. In der Realität wäre es unmöglich gewesen, unsere Order zu den festgelegten Preisen auszuführen.

Verwenden Sie den Modus "Every tick" deshalb mit Bedacht. Sie sollten darauf achten, ob Ihre Strategie besonders anfällig für Preissprünge ist.

Der Testmodus mit vollständigen Balken ist viel sicherer. In diesem Modus müssen wir keine Pending Orders verwenden, um eine hohe Genauigkeit sicherzustellen. Wenn unsere Strategie einen Markteintritt bei 7.495 erfordern würde, müsste sie den Öffnungspreis jedes Balkens prüfen und warten, bis die erforderliche Ebene überstiegen wird, um bei der Öffnung eines Balkens zum aktuellen Preis zu kaufen. Im Modus mit abgeschlossenen Balken hätten wir nur dann herausgefunden, dass der Preis über dem gewünschten Niveau liegt, wenn sich ein neuer Balken um 19:01 Uhr geöffnet wäre, weil der Öffnungspreis des Balkens um 19:00 Uhr immer noch unterhalb von 7.495 Rubeln lag. Unser Abschluss hätte im Modus mit abgeschlossenen Balken somit so ausgesehen:

 

Abb. 24 Tatsächlicher Abschluss im Modus mit abgeschlossenen Balken

Obwohl das Ergebnis letztendlich immer noch negativ gewesen wäre, hat es einen bedeutenden Vorteil:

Durch das Testen auf vollständigen Balken wird sichergestellt, dass alle Abschlüsse zu den tatsächlichen Preisen getätigt werden. Damit kann dieser Modus zum Testen von Strategien auf Märkten mit geringer Liquidität verwendet werden.

Wenn Ihre Strategie auf einem Timeframe arbeitet, der höher als M1 ist, und es sich nicht erlauben kann, die Handelsbedingungen einmal pro Periode zu prüfen, probieren Sie den Testmodus "1 minute OHLC" aus. In diesem Modus wird jeder Balken ausschließlich auf Basis der Preise des M1-Diagramms erzeugt. Da alle Preise des M1-Diagramms historisch sind, liefert dieser Modus auch eine absolute Genauigkeit und kann als vernünftiger Testmodus für mittelfristig ausgelegte Strategien empfohlen werden.

Ich empfehle nicht die Nutzung des Modus "Every tick" beim Testen von Strategien für Märkte mit geringer Liquidität. Außerdem sollten solche Strategien keine Stop-Order in ihren Aktivitäten verwenden.

Sie möchten vielleicht einwenden, dass die Genauigkeit des Markteintritts ausschlaggebend für Handelssysteme ist und schon einige wenige Punkte sich stark auf die letztendlichen Ergebnisse auswirken können. Doch wenn wir auf das Gesetz der großen Zahlen zurückgreifen, stellen wir fest, dass der Unterschied zwischen einem berechneten und einem tatsächlichen Markteintritt nur eine Rauschkomponente ist. In den meisten Fällen geschieht der Markteintritt bei viel schlechteren Preisen als den berechneten, obwohl der Extremwert des Preises manchmal das berechnete Niveau durchbricht, nur um dann noch innerhalb desselben Balkens umzukehren.

Hätten wir den Modus "Every tick" angewendet, hätten wir in solchen Momenten Verluste erlitten. Hätten wir hingegen vollständige Balken genutzt, hätten wir den Markt unter solchen Bedingungen überhaupt nicht betreten. In anderen Worten: Der Markteintritt bei schlechteren Preisen wäre durch andere (positive) Effekte kompensiert worden. Im Allgemeinen wäre der Unterschied vollständig beseitigt worden und das Ergebnis würde vollständig von der in der Strategie umgesetzten Idee abhängen, anstatt eines augenblicklichen Eintrittsniveaus.


Fazit

Es ist nun an der Zeit, die wesentlichen Ideen zusammenzufassen:

  • Der Marktpreis ist diskret. Er besteht aus einer Vielzahl von Abschlüssen, die die Marktdiagramme formen, welche diese Diskretheit verbergen. Bei der Analyse eines Preisbalkens können wir nicht zuverlässig bestimmen, welche Preise bis zu seiner Vervollständigung aufgetreten sind. Bei der ersten Annäherung wird angenommen, dass die Liquidität von Balken endlos ist und ein Bereich jedes Balkens gleichmäßig mit Preisen gefüllt wird, sodass jedes Preisniveau einen tatsächlichen Abschluss darstellt.

  • Auf Märkten mit geringer Liquidität kann die Diskretisierung von Marktpreisen sehr hoch sein. Es wird also empfohlen, Limit-Order für Marktein- und -austritte zu nutzen. Mit Limit-Ordern können wir die Diskretheit der Marktpreise überwinden und übermäßige Slippage vermeiden.

  • MetaTrader 5 verfügt über spezielle Buy-Stop-Limit- und Sell-Stop-Limit-Order, durch die herkömmliche Stop-Loss-Ebenen ersetzt werden können. Stop-Limit-Order sind sicher und können genutzt werden, um die maximale Slippage zu steuern, selbst wenn das Terminal nicht ausgeführt wird.

  • Neben ihrer Diskretheit haben Marktpreise eine gewisse Liquidität, die manchmal berücksichtigt werden sollte. Die aktuelle Liquidität wirkt sich auf den Wert der Slippage aus. Dank der Kenntnis des Zustands der Markttiefe können wir die potenzielle Slippage berechnen.

  • Die Steuerung des aktuellen Spreads ist eine einfachere Möglichkeit, die potenzielle Liquidität zu bewerten, ohne die Markttiefe zu nutzen. Das Spread-Niveau hängt für gewöhnlich von der Liquidität eines Symbols ab. Wenn ein Spread zu hoch ist, ist es empfehlenswert, auf einen besseren Zeitpunkt für die Durchführung eines Abschlusses zu warten.

  • Der "Sleep"-Modus stellt einen zuverlässigen Schutzmechanismus gegen Preissprünge für Ihren EA dar. In diesem Modus wird die Handelslogik des EAs nur einmalig beim Eingang eines neuen Balkens geprüft. Außerdem sind EAs, die in diesem Modus entwickelt werden, für Tests im Modus mit abgeschlossenen Balken geeignet.

  • Der Modus mit abgeschlossenen Balken ist der genaueste Testmodus, da er bei seiner Arbeit diskrete und tatsächliche historische Preise anwendet. Zudem liefert dieser Modus eine hohe Testgeschwindigkeit. Vollständige Balken und "1 minute OHLC" sind die einzigen Testmodi, die für die Verwendung auf Märkten mit geringer Liquidität empfehlenswert sind.

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

Beigefügte Dateien |
MarketBook.mqh (10.83 KB)
PanelSL.mqh (12.59 KB)
MovingAverage.mq5 (3.99 KB)
Spread_Record.mq5 (4.38 KB)
Zeichnen von Messuhren mithilfe der Klasse CCanvas Zeichnen von Messuhren mithilfe der Klasse CCanvas

Wir finden Messuhren in Autos und Flugzeugen, in der industriellen Fertigung und in unserem Alltag. Sie werden in allen möglichen Bereichen eingesetzt, die eine schnelle Reaktion auf das Verhalten eines kontrollierten Werts erfordern. Dieser Beitrag beschreibt die Bibliothek der Messuhren für MetaTrader 5.

Zeichnen von Widerstands- und Unterstützungsebenen mithilfe von MQL5 Zeichnen von Widerstands- und Unterstützungsebenen mithilfe von MQL5

Dieser Beitrag beschreibt eine Methode zum Finden von vier Extrema zum Zeichnen von darauf basierenden Unterstützungs- und Widerstandsebenen. Zum Finden der Extrema in einem Diagramm eines Währungspaars wird der Indikator RSI verwendet. Als Beispiel wird ein Indikatorcode bereitgestellt, der die Unterstützungs- und Widerstandsebenen anzeigt.

Bau einer interaktiven Anwendung zur Anzeige von RSS-Feeds in MetaTrader 5 Bau einer interaktiven Anwendung zur Anzeige von RSS-Feeds in MetaTrader 5

In diesem Beitrag betrachten wir die Möglichkeit, eine Anwendung zur Anzeige von RSS-Feeds zu erzeugen. Es wird gezeigt, wie Aspekte der Standard Library dazu verwendet werden können, interaktive Programme für MetaTrader 5 zu erstellen.

Das MQL5-Kochbuch: ОСО-Orders Das MQL5-Kochbuch: ОСО-Orders

Die Handelsaktivitäten jedes Händlers haben immer mit verschiedenen Mechanismen und Verflechtungen zu tun, einschließlich Zusammenhängen bei Orders. Dieser Beitrag schlägt eine Lösung zur Verarbeitung von OCO-Orders vor. Hierbei spielen Standard Library-Klassen sowie auch neue Datentypen, die darin erzeugt werden, eine große Rolle.