English Русский 中文 Español 日本語 Português
Universeller Expert Advisor: Pending Orders und Hedging Support (Part 5)

Universeller Expert Advisor: Pending Orders und Hedging Support (Part 5)

MetaTrader 5Beispiele | 2 Juni 2016, 13:12
2 173 0
Vasiliy Sokolov
Vasiliy Sokolov

Inhaltsverzeichniss


Einleitung

Der neue Artikel der Serie, der der Entwicklung eines universellen Expert Advisor gewidmet ist, beinhaltet die Beschreibung einer weiteren Funktionalität der speziellen Trading Engine, mit welcher Sie ganz einfach einen Trading Roboter, welcher eine komplexe Logik benutzt, entwickeln können. Das Set der CStrategie-Klassen wird kontinuierlich verbessert. Es werden dieser Trading Engine neue Algorithmen hinzugefügt welche das Handeln effizienter und einfacher gestalten. Die weitere Entwicklung der Algorithmen resultiert im Wesentlichen aus dem Feedback von den Benutzern, die diese Serie von Artikeln lesen und ihre Fragen in den Foren oder auch durch persönliche Nachrichten stellen. Eine der am häufigsten gestellten Fragen hatte mit der Verwendung von Pending Orders zu tun. Da die CStrategy Klasse bisher kein komfortables Werkzeug für die Verwaltung von Pending Orders besaß, wurde dieses Thema in den vorherigen Artikeln noch nicht besprochen. Diese Artikel stellt nun bedeutende Ergänzungen der CStrategy Trading Engine vor. Nach all diesen Neuerungen, bietet die CStrategy nun Werkzeuge für das Arbeiten mit Pending Orders. Wie werden die Details dieser Neuerungen später besprechen.

Zusätzlich, bietet jetzt die neue Version der Metatrader Plattform 5 die Unterstützung für bidirektionales Trading mit Konten, welche die Hedging-Option aktiviert haben (Details hierzu finden Sie in dem Artikel "MetaTrader 5 unterstützt Hedging-Positionen"). Der Programmcode der CStrategy, wurde entsprechend modifiziert, damit die Innovationen der neuen Kontotypen einwandfrei unterstützt werden. Es waren nur wenige Änderung notwendig, um das Hedging unterstützen zu können, was darauf hindeutet, dass unser ursprünglicher Ansatz korrekt war: Änderungen und Ergänzungen, egal die global sie sind, stören die Operation der Trading Engine nicht. Darüber hinaus bieten die neuen MetaTrader 5 Werkzeuge, wie zum Beispiel das Hedging, viele interessante Möglichkeiten, die in den neuen Versionen der Strategie umgesetzt werden können.

Zusätzlich unterstützt die CStrategy jetzt auch die intuitiven und beliebten Methoden für die Abfrage der aktuellen Ask, Bit, und Last Kurse. Diese Methoden wurden in den ersten Kapitel dieses Artikels beschrieben.

Dieser Artikel beinhaltet eine Menge Informationen, damit alle Innovationen und Veränderungen in der CStrategy und Metatrader 5 abgedeckt werden. Dieser Artikel behandelt viele der Themen, die wir in den vorherigen Teilen bisher nicht besprochen haben. Ich hoffe, dieses wird Sie interessieren.


Zugriff auf die aktuellen Kurse über die Ask, Bid und Last-Methoden. Überschreiben der Digits Funktion.

Trader brauchen sehr oft den Zugriff auf die aktuellen Kurse. Die vorherigen Versionen der CStrategy, beinhalteten keine speziellen Methoden für solch einen Datenzugriff. Anstelle dessen, wurde angenommen, dass der Anwender die Standardfunktionen für die Abfrage von Kursen verwendet. Um zum Beispiel den Ask-Kurs herausfinden zu können, musste der Anwender den folgenden Programmcode schreiben:

double ask = SymbolInfoDouble(ExpertSymbol(), SYMBOL_ASK);
int digits = SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
ask = NormalizeDouble(ask, digits);

Auch wenn sie nur eine Funktion für die Abfrage des Brief Kurses benötigen — SymbolInfoDouble —, sollte der abgefragte Wert auf die Genauigkeit des aktuellen Finanzinstrumentes normalisiert werden. Somit benötigt die Abfrage des Ask-Kurses weitere Aktionen. Das gleiche gilt natürlich für Bits und Last ebenso.

Damit die Verwendung nun einfacher wird, wurden drei Methoden der ABC hinzugefügt: Ask(), Bid() und Last(). Dieses von denen erhält den entsprechenden Kurs und normalisiert ihn entsprechend dem aktuellen Symbol: 

//+----------------------------------------------------------------+
//| Gibt den Ask-Kurs zurück.                                      |
//+----------------------------------------------------------------+
double CStrategy::Ask(void)
  {
   double ask = SymbolInfoDouble(ExpertSymbol(), SYMBOL_ASK);
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   ask = NormalizeDouble(ask, digits);
   return ask;
  }
//+----------------------------------------------------------------+
//| Gibt den Bid-Kurs zurück.                                      |
//+----------------------------------------------------------------+
double CStrategy::Bid(void)
  {
   double bid = SymbolInfoDouble(ExpertSymbol(), SYMBOL_BID);
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   bid = NormalizeDouble(bid, digits);
   return bid;
  }
//+----------------------------------------------------------------+
//| Gibt den Last-Kurs zurück.                                     |
//+----------------------------------------------------------------+
double CStrategy::Last(void)
  {
   double last = SymbolInfoDouble(ExpertSymbol(), SYMBOL_LAST);
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   last = NormalizeDouble(last, digits);
   return last;
  }

Diese Methoden sind jetzt in der neuen CStrategy Klasse definiert und sind somit auch in allen von ihr abgeleiteten Strategien verfügbar. Wir werden diese in den Strategie-Beispielen verwenden.

Neben der Organisation des Zugriffs auf die Ask, Bid und Last Kurse über die entsprechenden Methoden, überschreibt die CStrategy noch die System- Digits Funktion. Diese Funktion gibt die Anzahl der Nachkommastellen des aktuellen Finanzinstrumente zurück. Es sieht zunächst so aus, als wäre das Überschreiben dieser Funktion unnötig, aber dieses stimmt nicht. Das Chart-Symbol, auf welchem der Expert Advisor gerade ausgeführt wird, kann sich von dem in der Strategie verwendeten Symbol unterscheiden. In so einem Fall kann der Aufruf der Digits-Systemfunktion zu falschen Ergebnissen führen Diese gibt dann die Anzahl der Digits von dem Symbol, auf welchem der EA gerade ausgeführt wird zurück, und nicht von dem Symbol mit dem gerade gearbeitet wird. Um diese Problematik zu umgehen, wird die Digits-Funktion in der CStrategy mit einer Methode des gleichen Namens überschrieben. Jede Referenz auf diese Funktion, führt nun die neue Methode aus. Sie gibt also jetzt die Anzahl der Nachkommastellen des Symbols zurück, das gerade in der Strategie des EA verwendet wird. Hier ist der Quellcode dieser Methode:

//+------------------------------------------------------------------------------------------+
//| Gibt die Anzahl der Nachkommastellen für das gerade verwendete Symbol zurück             |
//| instrument                                                                               |
//+------------------------------------------------------------------------------------------+
int CStrategy::Digits(void)
  {
   int digits = (int)SymbolInfoInteger(ExpertSymbol(), SYMBOL_DIGITS);
   return digits;
  }

Die müssen sich der Bedeutung der Überschreibung dieser Funktion Bewusst sein.

 

Unterstützung für Konten mit einer Hedging-Option

Der Handelsplattform Metatrader 5 wurde neulich die Unterstützung für Konten mit einer Hedging-Option hinzugefügt. Bei solchen Konten, kann ein Trader mehrere Positionen zu einem Symbol gleichzeitig offen haben. Sowohl als Kauf- als auch als Verkaufspositionen. In der CStrategy, werden alle Operationen mit Positionen in den speziellen Handlern SupportBuy und SupportSell durchgeführt. Die Positionen für die aktuelle Strategie, werden eine nach der anderen zu diesen Methoden geleitet. Es ist daher egal, ob es nur eine Position oder mehrere gibt. Positionen können mit dem gleichen Symbol oder mit unterschiedlichen Symbolen geöffnet werden. Der gleiche Mechanismus wird auch verwendet, um diese Positionen zu bearbeiten und weiterzuleiten. Daher wird es kleine Veränderungen geben, damit das Hedging unterstützt wird. Zunächst müssen wir die RebuildPosition Methode ändern. Wenn sich die Liste der gehaltenen Positionen ändert (Wenn neue Trades ausgeführt werden), ordnet diese Methode die Liste der Positionen neu. Die Neuordnung kann je nach verwendeter Methode unterschiedlich ausfallen. Für Verrechnungskonten, wird der Algorithmus mit der Selektion einer Position über das Symbol verwendet. Für Hedging-Accounts, wird der Index einer Position in der Liste verwendet.

Hier ist die vorherige Version der Methode RebuildPosition:

//+--------------------------------------------------------------------+
//| Neuordnung der Liste der Positionen.                               |
//+--------------------------------------------------------------------+
void CStrategy::RebuildPositions(void)
{
   ActivePositions.Clear();
   for(int i = 0; i < PositionsTotal(); i++)
   {
      string symbol = PositionGetSymbol(i);
      PositionSelect(symbol);
      CPosition* pos = new CPosition();
      ActivePositions.Add(pos);
   }
}

In der neuen Versionen werden in Abhängigkeit des Kontotyps zwei verschiedene Selektierungsalgorithmen verwendet:

//+--------------------------------------------------------------------+
//| Neuordnung der Liste der Positionen.                               |
//+--------------------------------------------------------------------+
void CStrategy::RebuildPositions(void)
  {
   ActivePositions.Clear();
   ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         string symbol=PositionGetSymbol(i);
         PositionSelect(symbol);
         CPosition *pos=new CPosition();
         ActivePositions.Add(pos);
        }
     }
   else
     {
      for(int i=0; i<PositionsTotal(); i++)
        {
         ulong ticket=PositionGetTicket(i);
         PositionSelectByTicket(ticket);
         CPosition *pos=new CPosition();
         ActivePositions.Add(pos);
        }
     }
  }

Beachten Sie, dass die gleiche Positions-Klasse CPosition Für die beiden unterschiedlichen Kontotypen verwendet wird. Sobald eine Position ausgewählt wurde, kann über die Methoden PositionGetInteger, PositionGetDouble und PositionGetString auf ihre Eigenschaften zugegriffen werden. Es ist an dieser Stelle egal, ob es sich um eine hedging- oder eine normale Position handelt Der Zugriff auf die Eigenschaften verläuft in beiden Fällen gleich. Aus diesem Grunde ist es möglich, die gleiche CPosition-Klasse bei verschiedenen Kontotypen verwenden zu können. 

Es gibt in der CStrategy Klasse keine weiteren Methoden die überschrieben werden müssen. Die CStrategy wurde so kreiert, dass die Operationen der Strategien, basierend auf dieser Engine, kontextabhängig ist. Das bedeutet, dass wenn eine Strategie auf einem Konto mit der Hedging-Option arbeitet und mehrere Positionen in eine Richtung geöffnet wurden, dass diese Positionen parallel verwaltet werden, wobei jede Position als eine seperate CPosition-Klasse behandelt wird. Im Gegensatz dazu, wenn nur eine Position in einem Account geöffnet werden kann, dann verwaltet die Strategie diese Position in Form des selben CPosition Objektes.  

Zu den Veränderungen in der RebuildPosition Methode, müssen wir noch den Inhalt einiger CPosition Methoden ändern. Diese Klasse befindet sich in der PositionMT5.mqh Datei, und sie beinhaltet Methoden die auf Systemfunktionen basieren. Die CPosition verwendet auch die Standard Trade-Klasse CTrade. Die neueste Version der CTrade wurde so modifiziert, dass sie ebenfalls die Verwendung der Eigenschaften von Hedging-Positionen erlaubt. Zum Beispiel kann eine Hedging-Position durch eine entgegengesetzte Position geschlossen werden, wofür die neue CTrade::PositionCloseBy Methode aufgerufen wird. Im folgenden nun Methoden der CPosition-Klasse, deren Inhalt geändert wurde:

//+------------------------------------------------------------------------+
//|                                                        PositionMT5.mqh |
//|                                       Copyright 2016, Vasiliy Sokolov. |
//|                                                    http://www.mql5.com |
//+------------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Object.mqh>
#include "Logs.mqh"
#include <Trade\Trade.mqh>
#include "Trailing.mqh"
//+------------------------------------------------------------------------+
//| Klasse für aktive Positionen für klassische Strategien                 |
//+------------------------------------------------------------------------+
class CPosition : public CObject
  {
   ...
  };
...
//+------------------------------------------------------------------------+
//| Gibt den absoluten Stop Loss Level der aktuellen Position zurück.      |
//| Wenn kein Stop Loss Level gesetzt wurde, gibt die Funktion 0.0 zurück  |
//+------------------------------------------------------------------------+
double CPosition::StopLossValue(void)
{
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_SL);
}
//+------------------------------------------------------------------------+
//| Legt einen absoluten Stop Loss Level fest                              |
//+------------------------------------------------------------------------+
bool CPosition::StopLossValue(double sl)
{
   if(!IsActive())
      return false;
   return m_trade.PositionModify(m_id, sl, TakeProfitValue());
}
//+------------------------------------------------------------------------+
//| Gibt den absoluten Stop Loss Level der aktuellen Position zurück.      |
//| Wenn kein Stop Loss Level gesetzt wurde, gibt die Funktion 0.0 zurück  |
//+------------------------------------------------------------------------+
double CPosition::TakeProfitValue(void)
{
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_TP);
}
//+------------------------------------------------------------------------+
//| Legt einen absoluten Stop Loss Level fest                              |
//+------------------------------------------------------------------------+
bool CPosition::TakeProfitValue(double tp)
  {
   if(!IsActive())
      return false;
   return m_trade.PositionModify(m_id, StopLossValue(), tp);
  }
//+------------------------------------------------------------------------+
//| Schließt die aktuelle Position mit dem Marktpreis und legt einen       |
//| Kommentar gleich 'comment' fest                                        |
//+------------------------------------------------------------------------+
bool CPosition::CloseAtMarket(string comment="")
  {
   if(!IsActive())
      return false;
   ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
      return m_trade.PositionClose(m_symbol);
   return m_trade.PositionClose(m_id);
  }
//+------------------------------------------------------------------------+
//| Gibt die Größe der aktuellen Position zurück.                          |
//+------------------------------------------------------------------------+
double CPosition::Volume(void)
  {
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_VOLUME);
  }
//+------------------------------------------------------------------------+
//| Gibt den aktuellen Profit einer Position in der Kontowährung zurück.   |
//+------------------------------------------------------------------------+
double CPosition::Profit(void)
  {
   if(!IsActive())
      return 0.0;
   return PositionGetDouble(POSITION_PROFIT);
  }
//+------------------------------------------------------------------------+
//| Gibt true zurück, falls die Position aktiv ist.  Returns false         |
//| im anderen Fall.                                                       |
//+------------------------------------------------------------------------+
bool CPosition::IsActive(void)
{
   ENUM_ACCOUNT_MARGIN_MODE mode=(ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
      return PositionSelect(m_symbol);
   else
      return PositionSelectByTicket(m_id);
}
//+------------------------------------------------------------------------+


Wie Sie sehen können, ist die Basis dieser Methode ein Aufruf einer anderen IsActive-Methode. Diese Methode gibt true zurück, falls die aktive Position welche durch das CPosition Objekt repräsentiert wird, in dem System auch wirklich existiert. Diese Methode selektiert eine Position unter Verwendung einer der beiden Methoden, in Abhängigkeit von dem Kontotyp. Bei klassischen Konten mit der Verrechnungs-Option, wird eine Position mit der PositionSelect Funktion unter Angabe des Symbols selektiert. Für Konten mit der Hedging-Option, wird eine Position über Ihr Ticket unter Verwendung der neuen PositionSelectByTicket-Funktion selektiert. Das Ergebnis (true or false), wird der aufrufenden Prozedur zurückgegeben. Diese Methode setzt den Kontext für weitere Operationen mit dieser Position. Es gibt keine Notwendigkeit, die Trading-Algorithmen zu modifizieren, da alle Trading-Funktionen der CPosition auf der CTrade-Klasse beruhen. CTrade kann bei beiden Kontotypen Orders modifizieren, öffnen und schließen.  

 

Die Verwendung von Pending-Orders in früheren Versionen der CStrategy

Die Verwendung von Pending Orders ist ein bedeutender Bestandteil von vielen Trading Algorithmen. Nach der Veröffentlichung der ersten Version der CStrategy Trading Engine, habe ich jede Menge Anfragen über die Verwendung von Pending Orders erhalten. In diesem und in den folgenden Abschnitten, werden wir das Handeln mit Pending Orders besprechen.

Die CStrategy Engine wurde ursprünglich ohne die Unterstützung von Pending Orders erstellt. Aber das bedeutet nicht automatisch, dass die entwickelte Strategie, basierend auf der CStrategy-Klasse nicht mit Pending Orders umgehen kann. Bei der Verwendung der ersten CStrategy Version, konnte dieses unter Zuhilfenahme der Standard MetaTrader 5 Funktionen, wie zum Beispiel OrdersTotal()und OrderSelect() erledigt werden.

Zum Beispiel kann ein Expert Advisor eine Pending-Stop-Order setzen oder eine zuvor aufegebene Order mit jeder neuen Bar so modifizieren, dass ihr Trigger-Kurs 0.25% höher als der aktuelle Kurs ist (für einen Kauf), or 0.25% tiefer ist (Für einen Verkauf) Die Idee dahinter ist, dass wenn der Preis eine plötzliche starke Kursbewegung innerhalb einer Bar ausführt, die Order ausgeführt wird und der Expert Advisor somit innerhalb der starken Kursbewegung in den Markt eintritt. Wenn die plötzliche Kursbewegung nicht stark genug ist, dann wird die Order nicht ausgeführt. In diesem Fall muss die Order auf einem neuen Level festgelegt werden. Die vollständige Implementierung dieser Strategie finden Sie unterhalb der Abschnitte, die diesen Algorithmus beschreiben. Ich habe sie CImpulse genannt. Wie der Name selbst schon sagt, basiert sie auf dem Impulse Algorithmus. Sie benötigt einen einfachen Einstieg in den Markt, nach dem Triggern einer pending Order. Wie wir bereits wissen, enthält die CStrategy für das Eingehen einer Position spezielle überschriebene Methoden: BuyInit und SellInit . Daher solltet der Algorithmus für die pending Orders diesen Methoden hinzugefügt werden. Ohne die direkte Unterstützung der CStrategy, würde der Programmcode für Kauf-Operationen wie folgt aussehen:

//+----------------------------------------------------------------------+
//| Die Logik der Operationen für Pending Orders (Kauf).                 |
//+----------------------------------------------------------------------+
void CMovingAverage::InitBuy(const MarketEvent &event)
  {
   if(!IsTrackEvents(event))return;                      // Behandle nur den benötigten event!
   if(positions.open_buy > 0) return;                    // Wenn es schon eine offene Position gibt, dann kaufen wir keine weitere!
   int buy_stop_total = 0;
   for(int i = OrdersTotal()-1; i >= 0; i--)
   {
      ulong ticket = OrderGetTicket(i);
      if(!OrderSelect(ticket))continue;
      ulong magic = OrderGetInteger(ORDER_MAGIC);
      if(magic != ExpertMagic())continue;
      string symbol = OrderGetString(ORDER_SYMBOL);
      if(symbol != ExpertSymbol())continue;
      ENUM_ORDER_TYPE order_type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
      if(order_type == ORDER_TYPE_BUY_STOP)
      {
         buy_stop_total++;
         Trade.OrderModify(ticket, Ask()*0.0025, 0, 0, 0);
      }
   }
   if(buy_stop_total == 0)
      Trade.BuyStop(MM.GetLotFixed(), Ask() + Ask()*0.0025, ExpertSymbol(), 0, 0, NULL);
  }

Die IsTrackEvents Methode identifiziert, dass der empfangene Event zu dem Öffnen einer neuen Bar des aktuellen Symbols gehört. Anschließend überprüft der Expert Advisor die Anzahl der offenen Positionen in Kauf-Richtung. Wenn es schon eine Long-Position gibt, dann sollte der EA keine weitere kaufen und die Logik ist vollständig abgearbeitet. Als nächstes überprüft die Strategie alle aktuellen Pending Orders. Sie durchläuft die Indexe der Pending Orders. Jede Order wird über ihren Index selektiert und anschließend wird die Magic-Number und das Symbol analysiert. Wenn diese zwei Parameter den Parametern des Expert Advisors entsprechen, dann wird angenommen, dass diese Order zu dem aktuellen Expert Advisor gehört und der Zähler für die Orders wird um einen erhöht. Die Order wird modifiziert, indem ihr Einstiegskurs mit einem Kurs der dem aktuellen Kurs zuzüglich 0.25% entspricht, geändert wird. Wenn es keine Pending Orders gibt, was auch damit festgestellt werden kann, dass der buy_stop_order Zähler Null ist, dann wird eine neue Order mit einem Abstand von 0.25% von dem aktuellen Kurs platziert.

Beachten Sie, dass es kein Öffnen von Positionen in der InitBuy gibt. Bei der CStrategy gibt es keine solche Beschränkungen für das Öffnen von Positionen. Daher kann hier jede Logik eines Expert-Advisors hinzugefügt werden. Aber für ein richtiges Event-Handling, muss die Logik präzise mit dem Öffnen einer Position verbunden werden, entweder über Pending- oder Market-Orders. 

Wie wir an diesem Beispiel sehen kommen, werden die Operationen mit Pending Orders ähnlich ausgeführt wie konventionelle Positionen. Die wichtigsten Voraussetzungen sind die gleichen: Es ist notwendig die Kauf- und Verkauf-Logik in zwei Methoden zu unterteilen BuyInit and SellInit. Nur Pending-Kauf-Orders sollten in der BuyInit bearbeitet werden. Und in der SellInit sollten nur Pending-Verkauf-Orders bearbeitet werden. Der Rest der Logik für die Operationen mit Pending Orders ist gleich dem klassischen Schema aus MetaTrader 4: Das Durchsuchen von Orders; das Selektieren von Orders, die zu dem aktuellen Expert Advisor gehören; die Analyse der Handels-Umgebung und die Entscheidung, eine Order zu öffnen oder eine existierende Order zu verändern. 

 

CPendingOrders Und COrdersEnvironment für das Arbeiten mit Pending-Orders

Die Standard MetaTrader5-Funktionen, für das Arbeiten mit Pending Orders, bieten ein benutzerfreundliches System für eine umfassende Überwachung der Pending Orders. Aber die CStrategy ist eine Zusammenstellung von objektorientierten Klassen, welche es erlaubt, ein Handelssystem zu erzeugen. Alle Aktionen, die in der CStrategy ausgeführt werden, sind objektorientiert. Das heißt, sie werden durch Objekte ausgeführt die wiederum die Handelsoperationen ausführen. Diese Ansatz bietet viele Vorteile. Hier sind einige von ihnen:

  • Reduzierte Größe des benutzerdefinierten Programmcodes. Viele Aktionen, wie zum Beispiel die Normalisierung des Kurses oder die Vorbereitung von Arrays für die CopyBuffer-Klassen-Funktionen werden "hinter den Kulissen" ausgeführt, während die Methode ein fertiges Ergebnis liefert. Somit müssen Sie keine weiteren Prozeduren für die Überprüfung von Ergebnissen schreiben oder Zwischenschritte, welche nicht verhindert werden können, wenn sie nur mit den MetaTrader 5 Systemfunktionen arbeiten.
  • Plattformunabhängig. Da alle angebotenen Klassen und Methoden in der formalen MQL Sprache geschrieben worden sind, ist es möglich, alle Eigenschaften über universelle Methoden zu erhalten. Aber die aktuelle Implementation dieser Methoden ist bei den verschiedenen Plattformen unterschiedlich. Aber dieses spielt auf der Ebene des Endanwenders keine Rolle. Das bedeutet, dass ein Expert Advisor, der für die eine Plattform entwickelt wurde, theoretisch auch auf eine anderen Plattformen kompiliert werden kann. Aber in der Praxis gibt es viele kleine Nuancen, und wir wollen in diesem Artikel nicht die Plattformunabhängigkeit besprechen.
  • Funktionalität. Ein Set von MQL Funktionen bietet grundlegende Funktionalitäten, und durch die Kombination können Sie komplexe Algorithmen und sinnvolle Funktionen erzeugen. Wenn diese Algorithmen in einer einzigen Klasse, wie der CStrategy bereitgestellt werden, wird die Funktionalität noch einfacher zugreifbar, während das Hinzufügen von neuen Funktionen die Operationen nicht verkompliziert, da in so einem Fall nur neue Module hinzugefügt werden, bei denen ein Entwickler eines EAs entscheiden kann, ob er sie verwenden will oder nicht.

Da wir bei diesem Ansatz bleiben wollen, haben wir uns dazu entschlossen, die Funktionalitäten der CStrategy um die objektorientierten Mechanismus für das Arbeiten mit Pending Orders zu erweitern. Dieser Mechanismus wird über zwei Klassen repräsentiert: CPendingOrder und COrdersEnvironment. COrder bietet ein praktisches Objekt, welches alle Eigenschaften einer Pending-Order enthält, auf welche über die OrderGetInteger, OrderGetDouble und OrderGetString zugegriffen werden kann. Der Zweck der COrdersEnvironment wird später besprochen.

Nehmen wir an, dass das CPendingOrder Objekt eine Pending-Order repräsentiert, welche aktuell im System existiert Wenn Sie die Pending-Order löschen, was soll dann mit dem CPendingOrder Objekt, welches diese Order repräsentiert, geschehen? Wenn dieses Objekt bestehen bleibt, nachdem die Order geschlossen wurde, würde dieses zu ernsten Fehlern führen. Der Expert Advisor würde das CPendingOrder-Objekt finden und fehlerhafterweise annehmen, dass diese Order in dem System noch existiert. Um solche Fehler zu vermeiden, müssen wir sicherstellen dass die Handelsumgebung mit dem CStrategy Objekt einwandfrei synchronisiert wird. Mit anderen Worten: Wir müssen einen Mechanismus entwickeln, welcher uns garantiert, dass wir nur auf Objekte zugreifen können, die in dem System auch wirklich existieren. Dieses erledigt die COrdersEnvironment Klasse. Ihre implementation ist einfach und sie erlaubt nur noch den Zugriff auf die CPendingOrders-Objekte, die aktuelle Pending-Orders repräsentieren.

Die Basis der COrdersEnvironment Klasse umfasst die GetOrder und Total Methoden. die erste order gibt das CPendingOrders Objekt zurück, welche eine pending-Order mit einem bestimmten Index in dem MetaTrader 5 System der Pending-Orders entspricht. Die zweite Methode gibt die gesamte Anzahl an Pending-Orders zurück. Nun wird es Zeit die Klasse im Detail zu untersuchen. Hier ist der Quellcode dieser Klasse:

//+----------------------------------------------------------------------+
//| Eine Klasse für Operationen mit Pending-Orders                       |
//+----------------------------------------------------------------------+
class COrdersEnvironment
{
private:
   CDictionary    m_orders;         // Die Gesamtanzahl aller Pending-Orders
public:
                  COrdersEnvironment(void);
   int            Total(void);
   CPendingOrder* GetOrder(int index);
};
//+----------------------------------------------------------------------+
//| Wir benötigen das aktuelle Symbol und die Magic-Number des EA        |
//+----------------------------------------------------------------------+
COrdersEnvironment::COrdersEnvironment(void)
{
}
//+----------------------------------------------------------------------+
//| Gibt eine Pending Order zurück                                       |
//+----------------------------------------------------------------------+
CPendingOrder* COrdersEnvironment::GetOrder(int index)
{
   ulong ticket = OrderGetTicket(index);
   if(ticket == 0)
      return NULL;
   if(!m_orders.ContainsKey(ticket))
      return m_orders.GetObjectByKey(ticket);
   if(OrderSelect(ticket))
      return NULL;
   CPendingOrder* order = new CPendingOrder(ticket);
   m_orders.AddObject(ticket, order);
   return order;
}
//+---------------------------------------------------------------------+
//| Gib die Anzahl an Pending Orders zurück                             |
//+---------------------------------------------------------------------+
int COrdersEnvironment::Total(void)
{
   return OrdersTotal();   
}

Die total Methode gibt die Anzahl der gerade existierenden Pending Orders zurück. Diese Methode gibt niemals einen falschen Wert zurück, da sie den Systemwert von der Methode OrdersTotal() verwendet.

Um mit der GetOrder Methode arbeiten zu können, benötigen wir den Index der Pending Order in dem System. Durch die Total-Methode kennen wir immer die genaue Anzahl an Orders und der Index einer benötigten Order ist ebenfalls bekannt, denn dieser entspricht exakt dem aktuellen Index einer Pending Order in dem System. Zudem erhält die GetOrder-Methode einen Identifizierer einer Pending Order über ihren Index Falls eine Order aus dem System entfernt wurde, dann ist die Order ID = 0, und daher wird auch dem Expert Advisor einen Null-Wert übergeben, welcher darauf hindeutet, dass die gewünschte Order mit dem angegebenen Index nicht gefunden werden kann.

Jedes dynamisch erstellte Objekt muss explizit von einem speziellen "delete operator" entfernt werden. Da die GetOrder CPendingOrders Objekte dynamisch unter Verwendung des "new"-Operators erzeugt, müssen diese Objekte ebenfalls wieder gelöscht werden. Damit der User nicht jedes Objekt nach seiner Erstellung wieder entfernen muss, haben wir eine spezielle Technik entwickelt. Das Objekt befindet sich in einem speziellen "dictionary Container" innerhalb des COrdersEnvironment Objektes. Auf ein Element in dem dictionary kann über einen eindeutigen Schlüssel zugegriffen werden, welches in unserem Fall die Order ID darstellt. In diesem Fall, falls die Order noch existiert, ist das zuvor erzeugte Objekt das Objekt, welches diese Order repräsentiert und es wurde in dem Container abgelegt. Somit wird das zuvor erzeugte Objekt über die GetOrder-Funktion zurückgegeben. Falls es sich um den ersten Aufruf einer Order mit dieser ID handelt, wird ein neues CPendingOrder Objekt erstellt, und anschließend zu dem Dictionary hinzugefügt, und es wird dem Anwender eine Referenz zu diesem Objekt zurückgegeben.

Das haben wir nun von diesem vorgeschlagenen Ansatz? Erstens, gibt uns die GetOrder-Methode nur ein Objekt zurück, das auch wirklich als Pending-Order existiert. Hierfür ist die Systemfunktion OrderGetTicket verantwortlich. Zweitens, wird das Objekt nur erzeugt, falls es bisher noch nicht erzeugt worden ist. Dieses spart zusätzlich Recourcen. Und schließlich, drittens, befreit dieser Algorithmus den Anwender davon, die Objekte zu entfernen. Da alle Objekte in dem Dictionary abgespeichert werden, werden Sie automatisch nach der Deinitialisierung von COrdersEnvironment entfernt. 

 

Referenz zu den Pending-Orders in dem Programmcode des Expert Advisor

Nun ist es an der Zeit, die Logik für die Operationen mit Pending Orders, welche in den in dem vorherigen Abschnitt "Der Handel mit Pending Orders in früheren Versionen der CStrategy" besprochen worden ist neu zu schreiben . Hier ist der Programmcode mit den CPendingOrder und COrdersEnvironment Klassen:

//+------------------------------------------------------------------+
//| Wir kaufen, wenn der schnelle MA oberhalb des Langsamen ist.     |
//+------------------------------------------------------------------+
void CMovingAverage::InitBuy(const MarketEvent &event)
  {
   if(!IsTrackEvents(event))return;                      // Behandle nur den benötigten event!
   if(positions.open_buy > 0) return;                    // Wenn es schon eine offene Position gibt, dann kaufen wir keine weitere!
   int buy_stop_total = 0;
   for(int i = PendingOrders.Total()-1; i >= 0; i--)
     {
      CPendingOrder* Order = PendingOrders.GetOrder(i);
      if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
         continue;
      if(Order.Type() == ORDER_TYPE_BUY_STOP)
       {
         buy_stop_total++;
         Order.Modify(Ask() + Ask()*0.0025);
       }
       //delete Order; Es gibt keine Notwendigkeit das Objekt zu löschen!
     }
   if(buy_stop_total == 0)
      Trade.BuyStop(MM.GetLotFixed(), Ask() + Ask()*0.0025, ExpertSymbol(), 0, 0, NULL);
  }

Das PendingsOrders Objekt ist eine COrdersEnvironment Klasse. Die Suche durch Pending Orders startet auf die gleiche Art und Weise wie wenn man Systemfunktionen verwendet. Anschließend wird der Versuch unternommen, ein Order-Objekt zu erhalten, bei dem der Index der Pending Order gleich der Variablen i ist. Wenn das oder Objekt nicht erhalten werden konnte, weil es zum Beispiel zu einem anderen Expert Advisor gehört, dann wird die Suche fortgesetzt. Bei dieser Suche wird die IsMain Methode des CPendingorder Objektes verwendet. Sie Sie gibt true zurück, falls das Symbol und die Magic Number von der Order mit dem Symbol und der Magic Number des EAs übereinstimmen, was bedeutet, dass diese Order zu diesem Expert Advisor gehört.

Falls der Typ der Order dem Typ ORDER_TYPE_BUY_STOP entspricht, bedeutet das, dass die platzierte Order noch nicht getriggert worden ist und ihr Level mit der folgenden Formel modifiziert werden sollte: Aktueller Kurs + 0.25%. Das Modifizieren von Pending Orders wird mithilfe der Modify Methode und ihrer überschriebenen Version durchgeführt. Sie erlaubt es Ihnen, den Kurs und andere Parameter zu verändern.

Beachten Sie, dass sie das Objekt nach dem erfolgreichen abschließen aller Operationen nicht löschen müssen. Die Referenz zu dieser Order sollte so stehen gelassen werden, wie sie ist. Der PendingOrders Container verwaltet Objekte vom Typ CPendingOrder und benötigt keine Entfernung der zurückgegebenen Objekte in den Funktionen des Anwenders. 

Wenn es keine Pending Orders gibt und der buy_stop_total Zähler gleich Null ist, dann wird eine neue Pending Order platziert. Eine neue Order wird über das Trade-Modul platziert, welches in den vorherigen Abschnitten dieses Artikels beschrieben wurde. 

In diesem objektorientierten Ansatz, wird auf die Eigenschaften der Pending Orders über die entsprechenden Methoden des CPendingOrders-Objektes zugegriffen. Diese Art des Zugriffs verringert das Volumen des Programmcodes, während die Zuverlässigkeit bestehen bleibt, da PendingOrders garantiert, dass nur eine real bestehende Order empfangen werden kann, das heißt, dass die Pending Order auf jeden Fall in dem System existiert.

 

Die Handelslogik des CImpulse Expert Advisor, welcher Pending-Orders verwendet. 


Wir haben die Verwendung von Pending Orders nun analysiert, und jetzt können wir einen vollwertigen Expert Advisor schaffen, der die Möglichkeiten der Trading-Engine nutzt. Unsere Strategie basiert auf Einstiege in den Markt, während starke Kursbewegungen stattfinden, daher nennen wir sieCImpulse. Wenn eine neue Bar geöffnet wird, dann misst der EA einen vordefinierten Abstand von dem aktuellen Kurs, ausgedrückt in Prozent. Dann platziert in der EA eine Pending BuyStop und eine SellStop-Order, jeweils mit dem gleichen Abstand zum aktuellen Kurs. Der Abstand wird in Prozent angegeben. Wenn eine der Orders getriggert wird, dann bedeutet das, dass der Kurs einen Sprung gemacht hat, was auf einen Marktimpuls hindeutet. Die Order wird also ausgeführt und wechselt ihren Zustand in eine offene Position.

Die offene Positionen wird über einen einfachen gleitenden Durchschnitt verwaltet. Wenn der Kurs zu den gleitenden Durchschnitts zurückkehrt, dann wird die Position geschlossen. Der nachfolgende Screenshot zeigt den typischen Einstieg in eine Long-Position nach dem Triggern an einer Pending BuyStop order:


Abbildung 1. Eine Long-Position der CImpulse-Strategie.
 

In dem oben gezeigten Screenshot wird der Beginn einer Bar mit einem schwarzen Dreieck markiert. An diesem Punkt werden zwei Pending Orders mit einem Abstand von 0,1% des Bar-Open-Kurses platziert. Eine von ihnen — die BuyStop-Order — wurde getriggert. Somit wurde eine neue Long-Position geöffnet. Sobald eine Bar unterhalb des gleitenden Durchschnitts schließt, dargestellt als rote Linie, dann schließt der Expert Advisor die Position. In Abbildung 1 wird das Schließen der Position als blaues Dreieck dargestellt.

Falls eine Pending Order innerhalb einer Bar nicht getriggert wurde, dann wird ihr ein neuer Level zugewiesen, der über den neuen Kurs berechnet wird.

Die beschriebene Strategie hat eine spezielle Eigenschaft bei der Behandlung der Pending Orders. Der Level einer BuyStop order kann niedriger sein, als der aktuelle gleitende Durchschnitt. In so einem Fall, wird die Position direkt nach dem Öffnen wieder geschlossen, da der aktuelle Kurs unterhalb des gleitenden Durchschnitts liegt. Das gleiche trifft auch auf Short-Positionen zu. Der Trigger-Kurs einer SellStop-Oder kann höher sein, als der gleitende Durchschnitt. Um dieses zu vermeiden, benötigen wir eine zusätzlichen Überprüfung des Levels des gleitenden Durchschnitts in den BuyInit und SellInit Methoden. Somit wird der Algorithmus nur BuyStop-Orders platzieren, wenn sich der aktuelle Kurs über dem gleitenden Durchschnitt befindet. Das gleiche gilt für SellStop-Orders: Sie werden dann nur platziert, wenn sich der aktuelle Kurs unterhalb des gleitenden Durchschnitts befindet.

Wir werden auch die neue Eigenschaft des MetaTrader 5 Terminal verwenden, die Operation mit Hedging-Accounts. Dieses bedeutet, dass gleichzeitig eine Long- und eine Short-Position innerhalb einer Bar geöffnet werden können. Aber die ursprünglich präsentierte Logik des Expert Advisors, welcher die Long- und Short-Positionen trennt, macht es möglich, den Programmcode der Trading Strategie unverändert zu lassen. Unabhängig von der Anzahl der Positionen, werden alle Short-Positionen geschlossen, sobald sich der Kurs der Bar oberhalb des gleitenden Durchschnitts befindet und Long-Positionen werden geschlossen, wenn der Kurs unterhalb des gleitenden Durchschnitts fällt. In diesem Fall ist es egal, ob wir mit einem Hedging-Account arbeiten oder nicht. 

Zunächst öffnen wir einen Account mit der aktivierten Hedging-Optionen, indem wir das entsprechende Flag in dem "Account opening" Fenster markieren:

 

Abbildung 2. Das Öffnen eines Accounts mit der Hedging-Option 

Nach dem Öffnen des Accounts können wir mit dem Handel beginnen. Zunächst müssen wir die CImpulse Klasse schreiben, welche die Logik, die wir oben besprochen haben, implementiert. 

 

Die CImpulse Strategie-Klasse

Im Folgenden, sehen Sie das Listing der CImpulse Klasse, welche die Logik unseres neuen Expert Advisors beschreibt. Um die Operationen mit Pending Orders besser beschreiben zu können, wurde diese Klasse so einfach wie möglich gehalten. Sie enthält auch keine Prozeduren die sich mit dem Logging beschäftigen, sowie auch keine speziellen Methoden, die Strategie-Parameter von XML-Datei übersetzen:

//+------------------------------------------------------------------+
//|                                                      Impulse.mqh |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#include <Strategy\Strategy.mqh>
#include <Strategy\Indicators\MovingAverage.mqh>

input double StopPercent = 0.05;
//+------------------------------------------------------------------+
//| Definiert die Aktionen die für das Handeln mit Pending Orders    |
//| benötigt werden.                                                 |
//+------------------------------------------------------------------+
enum ENUM_ORDER_TASK
{
   ORDER_TASK_DELETE,   // Delete a Pending Order
   ORDER_TASK_MODIFY    // Modify a Pending Order
};
//+------------------------------------------------------------------+
//| Die CImpulse Strategie                                           |
//+------------------------------------------------------------------+
class CImpulse : public CStrategy
{
private:
   double            m_percent;        // Der Prozentwert für den Level einer Pending Order
   bool              IsTrackEvents(const MarketEvent &event);
protected:
   virtual void      InitBuy(const MarketEvent &event);
   virtual void      InitSell(const MarketEvent &event);
   virtual void      SupportBuy(const MarketEvent &event,CPosition *pos);
   virtual void      SupportSell(const MarketEvent &event,CPosition *pos);
   virtual void      OnSymbolChanged(string new_symbol);
   virtual void      OnTimeframeChanged(ENUM_TIMEFRAMES new_tf);
public:
   double            GetPercent(void);
   void              SetPercent(double percent);
   CIndMovingAverage Moving;
};
//+------------------------------------------------------------------+
//| Arbeiten mit pending BuyStop-Orders für das Öffnen einer Long-   |
//| position                                                         |
//+------------------------------------------------------------------+
void CImpulse::InitBuy(const MarketEvent &event)
{
   if(!IsTrackEvents(event))return;                      
   if(positions.open_buy > 0) return;                    
   int buy_stop_total = 0;
   ENUM_ORDER_TASK task;
   double target = Ask() + Ask()*(m_percent/100.0);
   if(target < Moving.OutValue(0))                    // Der Trigger-Kurs muss sich oberhalb des gleitenden Durchschnitts befinden
      task = ORDER_TASK_DELETE;
   else
      task = ORDER_TASK_MODIFY;
   for(int i = PendingOrders.Total()-1; i >= 0; i--)
   {
      CPendingOrder* Order = PendingOrders.GetOrder(i);
      if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
         continue;
      if(Order.Type() == ORDER_TYPE_BUY_STOP)
      {
         if(task == ORDER_TASK_MODIFY)
         {
            buy_stop_total++;
            Order.Modify(target);
         }
         else
            Order.Delete();
      }
   }
   if(buy_stop_total == 0 && task == ORDER_TASK_MODIFY)
      Trade.BuyStop(MM.GetLotFixed(), target, ExpertSymbol(), 0, 0, NULL);
}
//+------------------------------------------------------------------+
//| Arbeiten mit den pending SellStop-Orders zum Öffnen einer Short- |
//| position                                                         |
//+------------------------------------------------------------------+
void CImpulse::InitSell(const MarketEvent &event)
{
   if(!IsTrackEvents(event))return;                      
   if(positions.open_sell > 0) return;                    
   int sell_stop_total = 0;
   ENUM_ORDER_TASK task;
   double target = Bid() - Bid()*(m_percent/100.0);
   if(target > Moving.OutValue(0))                    // Der Trigger Kurs muss sich unterhalb des gleitenden Durchschnitts befinden.
      task = ORDER_TASK_DELETE;
   else
      task = ORDER_TASK_MODIFY;
   for(int i = PendingOrders.Total()-1; i >= 0; i--)
   {
      CPendingOrder* Order = PendingOrders.GetOrder(i);
      if(Order == NULL || !Order.IsMain(ExpertSymbol(), ExpertMagic()))
         continue;
      if(Order.Type() == ORDER_TYPE_SELL_STOP)
      {
         if(task == ORDER_TASK_MODIFY)
         {
            sell_stop_total++;
            Order.Modify(target);
         }
         else
            Order.Delete();
      }
   }
   if(sell_stop_total == 0 && task == ORDER_TASK_MODIFY)
      Trade.SellStop(MM.GetLotFixed(), target, ExpertSymbol(), 0, 0, NULL);
}
//+---------------------------------------------------------------------------------------+
//| Die Verwaltung einer Long-Position in Zusammenhang mit dem gleitenden Durchschnitt    |
//+---------------------------------------------------------------------------------------+
void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   if(Bid() < Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+---------------------------------------------------------------------------------------+
//| Die Verwaltung einer Short-Position in Zusammenhang mit dem gleitenden Durchschnitt   |
//+---------------------------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   if(Ask() > Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+---------------------------------------------------------------------------------------+
//| Filter an der eingehenden Events. Wenn das eingehende Event durch die Strategie nicht |
//| berarbeitet wurde, dann wird false zurückgegeben; Wenn es bearbeitet wurde            |
//| wird true zurückgeben.                                                                |
//+---------------------------------------------------------------------------------------+
bool CImpulse::IsTrackEvents(const MarketEvent &event)
  {
//Wir behandeln nur das Öffnen einer neuen Bar des aktuellen Symbols und der aktuellen Timeframe
   if(event.type != MARKET_EVENT_BAR_OPEN)return false;
   if(event.period != Timeframe())return false;
   if(event.symbol != ExpertSymbol())return false;
   return true;
  }
//+---------------------------------------------------------------------------------------+
//| Reaktionen auf den Wechsel des Symbols                                                |
//+---------------------------------------------------------------------------------------+
void CImpulse::OnSymbolChanged(string new_symbol)
  {
   Moving.Symbol(new_symbol);
  }
//+---------------------------------------------------------------------------------------+
//| Reaktion auf den Wechsel der Timeframe                                                |
//+---------------------------------------------------------------------------------------+
void CImpulse::OnTimeframeChanged(ENUM_TIMEFRAMES new_tf)
  {
   Moving.Timeframe(new_tf);
  }
//+--------------------------------------------------------------------------------------+
//| Gibt den Prozentsatz des breakthrough-Levels                                         |
//+--------------------------------------------------------------------------------------+ 
double CImpulse::GetPercent(void)
{
   return m_percent;
}
//+--------------------------------------------------------------------------------------+
//| Setzt den Prozentsatz des Breakthrough-Levels                                        |
//+--------------------------------------------------------------------------------------+
void CImpulse::SetPercent(double percent)
{
   m_percent = percent;
}

Die Standard mql5 Datei des Expert Advisors, der die Strategie als einen Expert Advisor konfiguriert und startet, wird im Folgenden gezeigt:

//+------------------------------------------------------------------+
//|                                                ImpulseExpert.mq5 |
//|                                 Copyright 2016, Vasiliy Sokolov. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+ 
#property copyright "Copyright 2016, Vasiliy Sokolov."
#property link      "http://www.mql5.com"
#property version   "1.00"
#include <Strategy\StrategiesList.mqh>
#include <Strategy\Samples\Impulse.mqh>

CStrategyList Manager;
//+------------------------------------------------------------------+
//| Expert Initialisierungs-Funktion                                 |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   CImpulse* impulse = new CImpulse();
   impulse.ExpertMagic(1218);
   impulse.Timeframe(Period());
   impulse.ExpertSymbol(Symbol());
   impulse.ExpertName("Impulse");
   impulse.Moving.MaPeriod(28);                      
   impulse.SetPercent(StopPercent);
   if(!Manager.AddStrategy(impulse))
      delete impulse;
//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert tick Funktion                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   Manager.OnTick();
  }
//+------------------------------------------------------------------+


 

Analyse der CImpulse Strategie

Bitte sehen Sie sich das beigefügte Video an, welches die Operation des EA unter Verwendung von Pending Orders innerhalb des Strategietesters demonstriert. BuyStop und SellStop werden platziert, sobald eine neue Bar geöffnet wird. Die Orders werden mit einem bestimmten Abstand vom aktuellen Kurs platziert, und bilden somit eine Art dynamischen Kanal. Sobald ein Level einer Pending Order unterhalb des Levels des gleitenden Durchschnitts fällt, wird sie vollständig entfernt. Aber wenn sich der Trigger-Kurs wieder oberhalb des gleitenden Durchschnitts befindet, dann wird eine neue Pending Order geöffnet. Dieselbe Regel gilt für SellStop-Orders:


Der nachfolgende Screenshot zeigt einen Zeitpunkt, zu dem sich eine Hedge-Situation gebildet hat. Das heißt, es gab eine offene Kauf-Position und das Schließen einer Short-Position wurde getriggert. Anschließend, blieb die Long-Position bestehen und wurde entsprechend ihrer Logik, wenn der Open Preis einer Bar unterhalb des gleitenden Durchschnitts fällt, geschlossen:

 

Abbildung 3. Positionsmanagement bei einem Hedge-Account.

Wenn wir die gleiche Logik dieses Expert Advisors bei einem klassischen Account anwenden, erhalten wir eine ähnliche, aber dennoch leicht unterschiedliche Situation:

 

Fig. 4. Positionsmanagement bei einem netting-Account (Verrechnung, klassisch). 

Bei einer starken Kursbewegung wird eine Kauf-Transaktion durchgeführt, und dieser Trade schließt gleichzeitig eine zuvor geöffnete Short-Position. So wurden alle offenen Positionen geschlossen. Daher öffnet der EA mit der nächsten Bar eine neue BuyOrder, die, nachdem sie getriggert wurde zu einer Long-Position wird und anschließend genauso wieder geschlossen wird, wie es bei dem Hedge-Account der Fall ist.

Wir können die Logik des EAs polymorph machen, damit der entsprechende Algorithmus in Abhängigkeit von der Art des Kontos ausgeführt wird. In einem Netting-Account kann es nur eine offene Position zu einem Symbol geben. Um zu vermeiden, dass das Öffnen einer neuen Position die entgegengesetzte Position schließt, sollten wir Stop-Loss zu allen Positionen hinzufügen. Der Stop Loss Wert sollte gleich dem Breakthrough-Level der entgegengesetzten Order sein. Somit würde das Triggern einer der Stop-Orders bedeuten, dass das StopLoss der entgegengesetzten Position getriggert wurde, falls eine solche Position zu diesem Moment existiert. Diese Logik wird natürlich nur bei Netting-Accounts aktiviert. Sie sollte den Methoden BuySupport und SellSupport hinzugefügt werden. Hier ist der modifizierte Quellcode der Methoden:

//+--------------------------------------------------------------------------------------+
//| Die Verwaltung einer Long-Position in Zusammenhang mit dem gleitenden Durchschnitt   |
//+--------------------------------------------------------------------------------------+
void CImpulse::SupportBuy(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Bid() - Bid()*(m_percent/100.0);
      if(target < Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Bid() < Moving.OutValue(0))
      pos.CloseAtMarket();
}
//+--------------------------------------------------------------------------------------+
//| Die Verwaltung einer Short-Position in Zusammenhang mit dem gleitenden Durchschnitt  |
//+--------------------------------------------------------------------------------------+
void CImpulse::SupportSell(const MarketEvent &event,CPosition *pos)
{
   if(!IsTrackEvents(event))return;
   ENUM_ACCOUNT_MARGIN_MODE mode = (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE);
   if(mode != ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
   {
      double target = Ask() + Ask()*(m_percent/100.0);
      if(target > Moving.OutValue(0))
         pos.StopLossValue(target);
      else
         pos.StopLossValue(0.0);
   }
   if(Ask() > Moving.OutValue(0))
      pos.CloseAtMarket();
}

Die Testergebnisse der Strategie mit den aktualisierten Funktionalitäten unterscheiden sich geringfügig. Bei Netting-Accounts, verhält sich der EA wie ein klassischer Expert Advisor, der mit nur einer offenen Position arbeitet:


Abbildung 5. Eine verwaltete Position von einem polymorphen Expert Advisor auf einem klassischen Netting-Account.

Die diesem Artikel beigefügten Dateien beinhalten die aktuellste Version der CImpulse-Strategie mit der Implementierung der Logik für unterschiedliche Account Typen.   

Beachten Sie, dass alle Versionen dieser Handelslogik korrekt sind. Aber die exakte Operation eine Handelsstrategie hängt von der Strategie selber ab. CStrategy bietet lediglich eine einheitliche Schnittstelle für das Arbeiten mit Positionen in Abhängigkeit von Ihrem Typ. 

 

Schlussfolgerung 

Wir haben die neuen Funktionen der CStrategy Trading Engine überprüft. Zu den neuen Funktionen gehören die Unterstützung für neue Kontoarten, Objekt-Operationen mit Pending Orders und einen erweiterten Satz von Funktionen für die Arbeit mit aktuellen Kursen.

Die neuen Methoden der CStrategy, ermöglichen einen schnellen und einfachen Zugang zu aktuellen Preisen, wie Ask, Bid, and Last. Die überschriebene Digits-Methode, gibt nun immer die korrekte Anzahl der Nachkommastellen des Preises des aktuellen Symbols zurück. 

Das Arbeiten mit Pending Orders unter Verwendung der speziellen CPendingOrdersC und COrdersEnvironment Klassen vereinfacht die Handelslogik. Der Expert Advisor kann auf eine Pending Order über das spezielle Objekt vom Typ CPendingOrder zugreifen. Durch die Veränderung der Eigenschaften des Objektes, zum Beispiel den Order-Trigger-Level, Verändert der EA automatisch die entsprechenden Eigenschaften der Order, die diesem Objekt entspricht. Dieses Modell von Objekten bietet ein hohes Maß an Zuverlässigkeit. Es ist unmöglich auf ein Objekt zuzugreifen, wenn es in dem System keinen entsprechenden tatsächlich laufenden Auftrag gibt, der diesem Objekt entspricht. Die Operationen mit Pending Orders werden in den BuyInit und SellInit Methoden ausgeführt, welche in der Strategie überschrieben werden sollten. BuyInit ist nur für Pending BuyStop and BuyLimit-Orders vorgesehen. SellInit ist nur für Pending SellStop and SellLimit-Orders vorgesehen.  

Mitte der vorgeschlagenen CStrategy Engine, gibt es praktisch keinen Unterschied zwischen der Arbeit mit einem Hedging-Account und einem klassischen Account. Operationen mit Positionen, hängen nicht von dem Typ der Position ab, und sie sind über die spezielle CPosition Klasse zugreifbar. Der einzige Unterschied bei den Operationen mit diesen Arten von Accounts, ist die Logik der Strategie. Wenn eine Strategie nur mit einer einzelnen Position arbeiten soll, dann muss sie auch die entsprechende Logik für die einwandfreie Verwaltung dieser Position implementieren. Wenn eine Strategie mit mehreren Positionen gleichzeitig arbeiten soll, dann muss die Logik berücksichtigen, dass entsprechende BuySupport und SellSupport Methoden benötigt werden. Die Trading Engine selbst, besitzt keine Handelslogik. Sie bietet lediglich den Typ der Position, die dem Kontotyp entspricht an.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2404

Beigefügte Dateien |
Bidirektionaler Handel und Absicherung von Positions in MetaTrader 5 mit Hilfe des HedgeTerminal Panels - Teil 1 Bidirektionaler Handel und Absicherung von Positions in MetaTrader 5 mit Hilfe des HedgeTerminal Panels - Teil 1
Dieser Beitrag beschreibt einen neuen Ansatz zur Absicherung von Positions und zieht diesbezüglich einen klaren Schlussstrich in den Diskussionen zwischen MetaTrader 4- und MetaTrader 5-Anwendern. Die Algorithmen, die derartige Absicherungen zuverlässig machen, werden in für Laien leicht verständlichen Begriffen beschrieben und anhand von einfachen Charts und Diagrammen veranschaulicht. Dieser Beitrag widmet sich dem neuen HedgeTerminal Panel, im Grunde ein voll funktionsfähiges Handelsterminal in MetaTrader 5. Mit Hilfe des HedgeTerminal und der von ihm zur Verfügung gestellten Visualisierung des Handels, können Positions so verwaltet werden, wie man es bereits aus MetaTrader 4 kennt.
Der MQL5-Assistent: Platzierung von Order, Stop-Losses und Take Profits auf berechneten Kursen. Erweiterung der Standard-Library Der MQL5-Assistent: Platzierung von Order, Stop-Losses und Take Profits auf berechneten Kursen. Erweiterung der Standard-Library
Dieser Artikel beschreibt die MQL5 Standard Library-Erweiterung, mit der Expert Advisors erzeugt und Order sowie Stop Losses und Take Profits mittels des MQL5-Assistenten nach Kursen, die von den mit aufgenommenen Modulen empfangen werden, platziert werden können. Dieser Ansatz bedeutet keinerlei zusätzliche Beschränkungen hinsichtlich der Menge an Modulen und führt auch zu keinerlei Konflikten bei ihrer gemeinsamen Arbeit.
Programmierung von EA-Modi mit Hilfe des Objekt-orientierten Ansatzes Programmierung von EA-Modi mit Hilfe des Objekt-orientierten Ansatzes
Dieser Beitrag erklärt das Konzept des Programmierens eines Mulit-Modus Handelsroboters in MQL5. Jeder Modus wird mittels des Objekt-orientierter Ansatzes implementiert. Instanzen von sowohl Modus-Klassenhierarchie als als auch Klassen zum Testen werden angeboten. Das Programmieren in mehreren Modi von Handelsrobotern soll alle Besonderheiten jedes betrieblichen Modus eines in MQL5 geschriebenen EA berücksichtigen. Funktionen und Aufzählung werden zur Identifizierung des Modus erzeugt.
Wie man auf die MySQL-Datenbank von MQL5 (MQL4) aus zugreift Wie man auf die MySQL-Datenbank von MQL5 (MQL4) aus zugreift
Dieser Beitrag beschreibt die Entwicklung einer Schnittstelle zwischen MQL und der MySQL-Datenbank, diskutiert bestehende praktische Lösungen und bietet eine bequemere Art der Implementierung einer Library zur Arbeit mit Datenbanken an. Zudem enthält er eine detaillierte Beschreibung der Funktionen, der Struktur der Schnittstelle, Beispiele und einige der spezifischen Merkmale der Arbeit mit MySQL. Was die Software-Lösungen angeht, finden sich im Anhang an diesen Beitrag die Dateien der dynamischen Libraries, Dokumentationen und Script-Beispiele für die MQL4 und MQL5 Sprachen.