English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Gegenläufig gerichteter Handel und Sicherung von Positionen in MetaTrader 5 mithilfe der HedgeTerminalApi, Teil 2

Gegenläufig gerichteter Handel und Sicherung von Positionen in MetaTrader 5 mithilfe der HedgeTerminalApi, Teil 2

MetaTrader 5Handelssysteme | 27 Juni 2016, 12:50
1 000 0
Vasiliy Sokolov
Vasiliy Sokolov

Inhalt


Einleitung

Bei diesem Beitrag handelt es sich um die Fortsetzung des Artikels Gegenläufig gerichteter Handel und Sicherung von Positionen in MetaTrader 5 mithilfe der HedgeTerminalApi, Teil 1. Im zweiten Teil geht es um Fragen zur Einbindung unserer Expert-Systeme sowie anderer in MQL5 geschriebener Programme in die Bibliothek der HedgeTerminalApi. Dieser Beitrag widmet sich der Darstellung der Arbeit mit dieser Bibliothek. Mit ihrer Hilfe können Sie Expert-Systeme für den Handel in unterschiedliche Richtungen erstellen und in einer praktischen und einfachen Handelsumgebung arbeiten.

Neben der Beschreibung der Funktionen der Bibliothek selbst werden in diesem Beitrag auch die Grundlagen asynchroner Handelsoperationen sowie der mehrsträngigen Programmierung behandelt. Diese Beschreibungen befinden sich entsprechend im 3. und 4. Kapitel dieses Artikels. Deshalb ist dieses Material auch für Devisenhändler von Nutzen, die kein Interesse daran haben, in unterschiedliche Richtungen zu handeln, sondern lediglich neue Kenntnisse in Sachen asynchroner und mehrsträngiger Programmierung erlangen möchten.

Das im weiteren Verlauf vorgestellte Material richtet sich an im Algorithmen gestützten Handel erfahrene und mit der Programmiersprache MQL5 vertraute Devisenhändler. Wer MQL5 nicht kennt, sollte besser den ersten Teil dieses Beitrages zur Hand nehmen. Dort wird anhand einfacher Modelle und Abbildungen das Grundprinzip der Funktionsweise der Bibliothek und des HedgeTerminal-Bedienfeldes erläutert.


Kapitel 1. Das Zusammenspiel von automatischen Handelssystemen mit der HedgeTerminalApi und ihrem Bedienfeld

1.1. Installation der HedgeTerminalApi. Erster Aufruf der Bibliothek

Die Installation der HedgeTerminalApi unterscheidet sich dadurch von der des grafischen Bedienfeldes von HedgeTerminal (HT), dass sie nicht unmittelbar ausgeführt werden kann, da die Bibliothek in MetaTrader 5 nicht für sich allein aufgerufen werden kann. Stattdessen muss zunächst ein eigenes Expert-System programmiert werden, um die Funktion HedgeTerminalInstall() aus der Bibliothek abzurufen. Diese Funktion legt eine eigene Include-Datei Prototypes.mqh zur Beschreibung der in der HedgeTerminalApi verfügbaren Funktionen an.

Die Einrichtung der Bibliothek auf einem Ausgabegerät erfolgt in vier Schritten:

Schritt 1. Herunterladen der Bibliothek HedgeTerminalApi auf das entsprechende Ausgabegerät. Ablegen der Bibliothek auf Ihrem Ausgabegerät unter dem Pfad: \MQL5\Experts\Market\HedgeTerminalApi.ex5.

Schritt 2. Erstellung eines neuen auf dem Standardmuster (Template) beruhenden Expert-Systems mithilfe des MQL5-Assistenten. Der MQL5-Assistent erzeugt folgenden Programmcode:

//+------------------------------------------------------------------+
//|                                   InstallHedgeTerminalExpert.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
  }

Schritt 3. Das resultierende Expert-System muss lediglich die Funktion OnInit() aufweisen sowie eine Exportanweisung zur Beschreibung einer besonderen aus der Bibliothek HedgeTerminalApi exportierten Installationsfunktion HedgeTerminalInstall(). Diese Funktion muss unmittelbar in der Systemfunktion OnInit() gestartet werden. Der Quellcode, der diese Operationen ausführt, ist gelb markiert:

//+------------------------------------------------------------------+
//|                                   InstallHedgeTerminalExpert.mq5 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#import "HedgeTerminalAPI.ex5"
   void HedgeTerminalInstall(void);
#import

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   HedgeTerminalInstall();
   ExpertRemove();   
//---
   return(INIT_SUCCEEDED);
  }

Schritt 4. Das weitere Vorgehen hängt davon ab, ob diese Bibliothek vorhanden ist oder nicht. Wenn sie da ist, kann dieses Expert-System in Echtzeit unmittelbar aus dem Diagramm aufgerufen werden. In diesem Fall wird das Standardinstallationsprogramm für die gesamte HedgeTerminal-Produktreihe ausgeführt. Dieser Schritt ist leicht zu bewältigen, indem man den Anweisungen der Abschnitte 2.1 und 2.2 des ersten Teils dieses Beitrags folgt. Der Installationsassistent installiert alle erforderlichen Dateien, darunter auch die Include-Datei und die Datei mit dem Expert-System für die Überprüfung.

Wenn die Bibliothek nicht erworben wurde, und lediglich ihre Funktionsweise ausprobiert werden soll, kann das Expert-System zwar nicht in Echtzeit gestartet werden, aber die Bibliothek (Api) kann durch Aufrufen des Expert-Systems in dem Strategieprüfprogramm getestet werden. In diesem Fall wird das Installationsprogramm nicht gestartet. Im Prüfmodus arbeitet die HedgeTerminalApi autonom, es müssen also keine auf übliche Weise installierten Dateien benötigt. Folglich muss auch weiter nichts eingerichtet werden.

Nach Abschluss der Überprüfung des Expert-Systems oder seiner Ausführung wird in dem allgemeinen Dateiverzeichnis des Ausgabegerätes der Ordner \HedgeTerminal angelegt. Der übliche Pfad zu dem allgemeinen Dateiverzeichnis der MetaTrader-Instanzen auf dem Ausgabegerät (Terminal) ist c:\Benutzer\<Benutzername>\Anwendungsdaten\Roaming\MetaQuotes\Terminal\Common\Files\HedgeTerminal\, wobei <Benutzername> die Bezeichnung des aktuell verwendeten Benutzerkontos auf dem Ausgabegerät ist. Zu diesem Zeitpunkt müssen in dem Ordner \HedgeTerminal die Dateien \MQL5\Include\Prototypes.mqh und \MQL5\Experts\Chaos2.mq5 bereits vorhanden sein. Kopieren Sie diese Dateien in die entsprechenden Verzeichnisse auf Ihrem Ausgabegerät (Terminal): die Datei Prototypes.mqh nach \MetaTrader5\MQL5\Include und die Datei Chaos2.mq5 nach \MetaTrader5\MQL5\Experts.

Die Include-Datei Prototypes.mqh enthält die Beschreibung der aus der Bibliothek HedgeTerminalApi exportierten Funktionen. Ihr Zweck und ihre Beschreibung sind in dem jeweiligen Kommentar zu ihnen zu sehen.

Die Datei Chaos2.mq5 enthält ein Beispiel für das in dem Abschnitt Beispiel für die Arbeit mit der Funktion SendTradeRequest und dem HedgeTradeRequest-Gerüst am Beispiel des Expert-Systems Chaos II dargestellte Expert-System. Es hilft dabei, sich besser vorstellen zu können, wie die HedgeTerminalApi arbeitet und was erforderlich ist, um ein Expert-System zu programmieren, das die Virtualisierungstechnologien von HedgeTerminal nutzt.

Die kopierten Dateien sind für Ihre Expert-Systeme verfügbar, und um mit der Verwendung der Bibliothek beginnen zu können, genügt es, die Include-Datei in den Programmcode Ihres Expert-Systems einzubinden, etwa so, wie es dieses Beispiel zeigt:

#include <Prototypes.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   int transTotal = TransactionsTotal();
   printf((string)transTotal);
  }

Der oben angeführte Code beispielsweise bezieht die Gesamtzahl der aktiven Positionen und zeigt sie unter der Registerkarte „Expert-Systeme“ der MetaTrader 5-Instanz auf dem Ausgabegerät (Terminal) an.

Es ist wichtig zu bedenken, dass die tatsächliche Bereitstellung von HedgeTerminal im Moment des ersten Aufrufs einer seiner Funktionen erfolgt. Diese Bereitstellung wird üblicherweise als verzögert (Lazy Initialization) bezeichnet. Deshalb kann der erste Aufruf einer dieser Funktionen recht viel Zeit beanspruchen. Wenn beim ersten Aufruf ein schnelles Ansprechen benötigt wird, muss HedgeTerminal (HT) rechtzeitig bereitgestellt werden, zum Beispiel könnte die Funktion TransactionTotal() in dem Programmblock OnInit() aufgerufen werden.

Die verzögerte Bereitstellung ermöglicht den Verzicht auf eine ausdrückliche Bereitstellung aus dem Expert-System heraus. Das erleichtert die Arbeit mit HedgeTerminal wesentlich und macht seine vorherige Einrichtung überflüssig.


1.2. Integration von Expert-Systemen in das HedgeTerminal-Bedienfeld

Wenn Sie über das grafische HedgeTerminal-Bedienfeld und eine voll funktionsfähige Fassung der Bibliothek verfügen, die in Echtzeit aufgerufen werden kann, können Sie Ihre Expert-Systeme in dieses Bedienfeld so einbinden, dass alle von ihnen ausgeführten Handelsoperationen ebenfalls in dem Bedienfeld wiedergegeben werden. Die Integration selbst erfolgt vollständig im Hintergrund. Bei der Nutzung von Funktionen der HedgeTerminalApi erscheinen die von den automatischen Handelssystemen ausgeführten Operationen automatisch in dem Bedienfeld. Die Anschaulichkeit lässt sich jedoch noch erhöhen, indem jeder abgeschlossenen Transaktion die Bezeichnung des Expert-Systems beigefügt wird. Entfernen Sie dazu in der Datei Settings.xml den Kommentar von der folgenden Auszeichnung:

<Column ID="Magic" Name="Magic" Width="100"/>

Diese Auszeichnung befindet sich in den Abschnitten <Active-Position> und <History-Position>.

Jetzt sind die Kommentare weg, und die Auszeichnungen (Tags) werden in die Verarbeitung einbezogen. Nach dem erneuten Laden des Bedienfeldes erscheint in den Tabellen mit den aktiven Positionen und dem Positionsverlauf die neue Spalte Magic. Sie zeigt die magische Zahl des Expert-Systems an, zu dem die jeweilige Position gehört.

Um die geläufigere Bezeichnung des Expert-Systems anstelle seiner magischen Zahl wiederzugeben, muss der entsprechende Name in die Parallelbezeichnungsdatei ExpertAliases.xml eingetragen werden. Angenommen, statt der magischen Zahl 123847 soll der Name des Expert-Systems, z. B. ExPro 1.1, eingegeben werden, dann ist folgende Auszeichnung in die Datei aufzunehmen:

<Expert Magic="123847" Name="ExPro 1.1"></Expert>

Wenn das ordnungsgemäß erfolgt ist, erscheint nach dem Neuladen des Bedienfeldes anstelle der magischen Zahl der Name des Expert-Systems:

Abb. 1.  Anzeige der Bezeichnung des Expert-Systems anstelle von Magic

Abb. 1. Anzeige der Bezeichnung des Expert-Systems anstelle von Magic

Bedenken Sie bitte, dass der Datenaustausch zwischen dem Bedienfeld und den Expert-Systemen in Echtzeit erfolgt. Das bedeutet, dass, wenn Sie eine Position des Expert-Systems unmittelbar aus dem Bedienfeld schließen, beim nächsten Aufruf der Funktion TransactionsTotal() davon erfährt, dass die Position geschlossen worden ist. Das gilt auch umgekehrt: Nachdem das Expert-System seine Position geschlossen hat, verschwindet diese sofort aus der Registerkarte mit den aktiven Positionen.


1.3. Funktionsweise der HedgeTerminalApi

HedgeTerminal arbeitet nicht nur mit gegenläufig gerichteten Positionen sondern auch mit anderen Arten des Handels, wie offenen Aufträgen (Pending Orders), Abschlüssen und von einem Makler ausgeführten Operationen. Aus der Sicht von HedgeTerminal fallen sie alle in die einheitliche Gruppe der Transaktionen. Ein Abschluss, offener Auftrag oder eine gegenläufig gerichtete Position, sie alle sind Transaktionen. Allerdings kann keine Transaktion für sich allein existieren. Im Sinn der objektorientierten Programmierung lässt sich eine Transaktion am einfachsten als eine abstrakte Basisklasse vorstellen, aus der sich alle möglichen Erscheinungsformen des Handels in der Art von Abschlüssen und gegenläufig gerichteten Positionen ableiten lassen. Aus diesem Grund können alle Funktionen der HedgeTerminalApi unter bestimmten Bedingungen in folgende Gruppen unterteilt werden:

  1. Funktionen für die Suche und Auswahl von Transaktionen Das allgemeine Grundgerüst der Funktionen und die Arten der Arbeit mit ihnen stimmen nahezu vollständig mit denen der Funktionen OrderSend() und OrderSelect() in MetaTrader 4 überein;
  2. Funktionen zum Bezug der Eigenschaften der gewählten Transaktion. Jede Transaktion besitzt ihre eigenen Eigenschaften sowie eigene Funktionen, um diese Eigenschaften auszuwählen. Das allgemeine Grundgerüst der Funktionen und wie man mit ihnen arbeitet, erinnern an die Systemfunktionen von MetaTrader 5 für den Zugriff auf die Eigenschaften von Positionen, Abschlüssen und Aufträgen (in der Art von OrderGetDouble() oder HistoryDealGetInteger());
  3. In der HedgeTerminalApi kommt nur eine Handelsfunktion zur Anwendung: SendTradeRequest(). Mit ihrer Hilfe kann eine gegenläufig gerichtete Position ganz oder teilweise geschlossen werden. Sie ermöglicht ebenfalls die Änderung der StopLoss- und der TakeProfit-Grenze sowie des Schlusskommentars. Die Arbeit mit dieser Funktion ähnelt der mit der Funktion OrderSend() in MetaTrader 5;
  4. Die Funktion GetHedgeError() zum Bezug allgemeiner Fehlermeldungen und die Funktionen zur ausführlichen Analyse der Handelsoperationen von HedgeTerminal: TotalActionsTask() und GetActionResult(). Auch sie dienen zur Ermittlung von Fehlern. Für sie gibt es in MetaTrader 4 und 5 keine Entsprechungen.

Die Arbeit mit fast allen diesen Funktionen erinnert an die Arbeit mit den Systemfunktionen in MetaTrader 4 und 5. In der Regel geht zu Beginn einer Funktion irgendein Bezeichner (Wert aus einer Aufzählung) in sie ein und sie gibt den diesem entsprechenden Wert aus.

Jede Funktion hat ihre eigenen Aufzählungen. Das allgemeine Grundgerüst ihres Aufrufs sieht aus wie folgt:

<value> = Function(<identifier>);

Wir sehen uns das Beispiel des Bezugs eines unverwechselbaren Positionsbezeichners an. In MetaTrader 5 würde der Abruf der entsprechenden Eigenschaft etwa so aussehen:

ulong id = PositionGetInteger(POSITION_IDENTIFIER);

In HedgeTerminal dagegen hätte der Abruf eines vergleichbaren Bezeichners für eine gegenläufig gerichtete Position folgendes Aussehen:

ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID)

Wie wir sehen, stimmen die Grundprinzipien der Funktionen überein. Lediglich die Arten der Aufzählungen weichen voneinander ab.


1.4. Auswahl von Transaktionen

Die Auswahl einer Transaktion besteht im Durchsuchen des Transaktionsverzeichnisses, was der Auftragsuche in MetaTrader 4 ähnelt. Während in MetaTrader 4 jedoch nur nach Aufträgen gesucht wird, kann in HedgeTerminal alles Mögliche eine Transaktion darstellen, zum Beispiel ein offener Auftrag oder eine Sicherungsposition. Deshalb müssen die Transaktionen zunächst mithilfe der Funktion TransactionSelect() ausgewählt werden, um anschließend ihre Art mit der Funktion TransactionType() zu bestimmen.

Momentan sind zwei Aufstellungen von Transaktionen verfügbar: die aktiven Transaktionen und der (historische) Transaktionsverlauf. Mit welcher der beiden gearbeitet wird, bestimmt das Modifizierungssymbol ENUM_MODE_TRADES. Es ähnelt dem Modifizierungssymbol MODE_TRADES in MetaTrader 4.

Es folgt der allgemeine Algorithmus für die Suche und Auswahl der Transaktion:

1: for(int i=TransactionsTotal(MODE_TRADES)-1; i>=0; i--)
2:     {
3:      if(!TransactionSelect(i,SELECT_BY_POS,MODE_TRADES))continue;
4:      if(TransactionType()!=TRANS_HEDGE_POSITION)continue;
5:      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue;
6:      if(HedgePositionGetString(HEDGE_POSITION_SYMBOL) != Symbol())continue;
7:      if(HedgePositionGetInteger(HEDGE_POSITION_STATE) == POSITION_STATE_FROZEN)continue;
8:      ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID)
9:     }

Der Code liest die Aufstellung der aktiven Transaktionen in dem Zyklus for (Zeile 1) aus. Bevor die Arbeit mit der Transaktion weitergehen kann, muss sie mithilfe der Funktion TransactionSelect() (Zeile 3) ausgewählt werden. Aus diesen Transaktionen werden nur die gegenläufig gerichteten Positionen (Zeile 4) ausgewählt. Stimmen die magische Zahl der Position und ihr Kürzel nicht mit der magischen Zahl des aktuellen Expert-Systems und dem Kürzel, auf dem es ausgeführt wird, überein, wird die nächste Position herangezogen (Zeilen 5 und 6). Dann wird der unverwechselbare Bezeichner für die Position ermittelt (Zeile 8).

Die Zeile 7 verdient besondere Erwähnung. Die gewählte Position muss auf die Möglichkeit ihrer Veränderung überprüft werden. Wenn sich Position bereits in einem Veränderungsvorgang befindet, kann sie in dem aktuellen Arbeitsgang (Strang) nicht verändert werden, obwohl eine ihrer Eigenschaften abgerufen werden kann. Wenn die Position gesperrt ist, ist es besser, auf ihre Freigabe zu warten, um Zugriff auf ihre Eigenschaften zu erhalten, oder erneut zu versuchen sie zu ändern. Um herauszufinden, ob die Position geändert werden kann, wird die Eigenschaft HEDGE_POSITION_STATE verwendet.

Das Modifizierungssymbol POSITION_STATE_FROZEN zeigt an, dass die Position „eingefroren“ ist und nicht verändert werden kann. Das Modifizierungssymbol POSITION_STATE_ACTIVE dagegen signalisiert, dass die Position „aktiv“ und für Veränderungen offen ist. Diese Modifizierungssymbole werden in der Aufzählung ENUM_HEDGE_POSITION_STATE aufgeführt. Mehr darüber verrät der entsprechende Abschnitt.

Im Fall der Notwendigkeit einer Suche in dem Verlauf der zurückliegenden Transaktionen müsste das Modifizierungssymbol MODE_TRADES in den Funktionen TransactionTotal() und TransactionSelect() durch MODE_HISTORY ersetzt werden.

Eine Transaktion in HedgeTerminal kann in einer anderen angelegt werden. Das ist ein großer konzeptioneller Unterschied zu MetaTrader 5, wo keinerlei Verschachtelung möglich ist. Zum Beispiel besteht eine „historische“ gegenläufig gerichtete Position in HedgeTerminal aus zwei Aufträgen, von denen jeder eine beliebige Anzahl von Abschlüssen umfassen kann. Ein grafische Darstellung der Verschachtelung könnte etwa so aussehen:

Abb. 2. Verschachtelte Transaktionen

Abb. 2. Verschachtelte Transaktionen

Die Verschachtelung der Transaktionen lässt sich in dem grafischen Bedienfeld von HedgeTerminal gut verfolgen.

Die folgende Bildschirmaufnahme zeigt eine voll entfaltete zu dem Expert-System MagicEx 1.3 gehörende Position, die das schön verdeutlicht:

Abb. 3. Verschachtelte Transaktionen im HedgeTerminal-Bedienfeld

Abb. 3. Verschachtelte Transaktionen im HedgeTerminal-Bedienfeld

Man erhält Zugang zu den Eigenschaften einzelner Aufträge oder sogar einzelner Abschlüsse innerhalb einer gegenläufig gerichteten Position.

Dazu ist es erforderlich:

  1. die „historische“ Transaktion auszuwählen und sich davon zu überzeugen, dass es sich um eine gegenläufig gerichtete Position handelt,
  2. mithilfe der Funktion HedgeOrderSelect() einen der Aufträge zu dieser Position auszuwählen;
  3. eine der Eigenschaften dieses Auftrages abzurufen: die Anzahl der in ihm enthaltenen Abschlüsse;
  4. durch Dursuchen aller Abschlüsse einen der zu dem Auftrag gehörenden Abschlüsse auszuwählen;
  5. die erforderliche Eigenschaft dieses Abschlusses abzurufen.

Beachten Sie bitte, dass nach der Auswahl der Transaktion auf deren sie bestimmende Eigenschaften zugegriffen werden kann. Wenn es sich also bei der Transaktion um einen Auftrag handelt, lässt sich nach seiner Auswahl mithilfe der Funktion HedgeOrderSelect() in Erfahrung bringen, wie viele Abschlüsse er enthält (HedgeOrderGetInter(HEDGE_ORDER_DEALS_TOTAL)) oder wie hoch sein gewichteter Einstiegskurs war (HedgeDealGetDouble(HEDGE_DEAL_PRICE_EXECUTED)).

Versuchen wir den Kurs für den in der Bildschirmaufnahme rot gekennzeichneten Abschluss #1197610 herauszubekommen. Dieser Abschluss gehört zu einer gegenläufig gerichteten Position des Expert-Systems MagicEx 1.3.

Damit dieses Expert-System selbst auf seine Position und auf diesen Abschluss zugreifen kann, muss folgender Code geschrieben werden:

#include <Prototypes.mqh>

ulong Magic=5760655; // MagicEx 1.3.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+ 
void OnTick()
  {
   for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--)
    {
      if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue;        // Select transaction #i;
      if(TransactionType()!=TRANS_HEDGE_POSITION)continue;                 // If transaction is not position - continue;
      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue;  // If position is not main - continue;
      ulong id = HedgePositionGetInteger(HEDGE_POSITION_ENTRY_ORDER_ID);   // Get id for closed order;
      if(id!=5917888)continue;                                             // If id of position != 5917888 - continue;
      printf("1: -> Select position #"+(string)id);                        // Print position id;
      if(!HedgeOrderSelect(ORDER_SELECTED_CLOSED))continue;                // Select closed order or continue;    
      ulong order_id = HedgeOrderGetInteger(HEDGE_ORDER_ID);               // Get id closed order;
      printf("2: ----> Select order #" + (string)order_id);                // Print id closed order;
      int deals_total = (int)HedgeOrderGetInteger(HEDGE_ORDER_DEALS_TOTAL);// Get deals total in selected order;
      for(int deal_index = deals_total-1; deal_index >= 0; deal_index--)   // Search deal #1197610...
        {
         if(!HedgeDealSelect(deal_index))continue;                         // Select deal by index or continue;
         ulong deal_id = HedgeDealGetInteger(HEDGE_DEAL_ID);               // Get id for current deal;
         if(deal_id != 1197610)continue;                                   // Select deal #1197610;
         double price = HedgeDealGetDouble(HEDGE_DEAL_PRICE_EXECUTED);     // Get price executed;
         printf("3: --------> Select deal #"+(string)deal_id+              // Print price excecuted;
              ". Executed price = "+DoubleToString(price,0));
        }
     }
  }

Nach dem Aufrufen dieses Codes erscheint unter der Registerkarte „Expert-Systeme“ der MetaTrader 5-Instanz auf dem Ausgabegerät (Terminal) folgende Meldung:

2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1)      3: --------> Select deal #1197610. Executed price = 4735
2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1)      2: ----> Select order #6389111
2014.10.21 14:46:37.545 MagicEx1.3 (VTBR-12.14,D1)      1: -> Select position #5917888

Wie zu sehen, wählt das Expert-System zunächst die Position #5917888 aus und danach innerhalb dieser Position den eingehenden Auftrag mit der Nummer #6389111. Nach der Auswahl des Auftrags beginnt das Expert-System, die Abschlüsse auf der Suche nach dem Abschluss mit der Nummer 1197610 auszulesen. Sobald es ihn gefunden hat, bezieht es den Kurs zu dem der Abschluss getätigt wurde und gibt ihn im Protokoll aus.


1.5. Fehlercodes abrufen mithilfe von GetHedgeError()

Bei der Arbeit in einer HedgeTerminal-Umgebung kann es zu Fehlern und unvorhergesehenen Situationen kommen. Deswegen kommen besondere Funktionen zur Ermittlung und Analyse von Fehlern zur Anwendung.

Der simpelste Grund für eine Fehlermeldung ist das Vergessen der Auswahl einer Transaktion mithilfe der Funktion TransactionSelect(). In diesem Fall gibt die Funktion TransactionType() das Modifizierungssymbol TRANS_NOT_DEFINED aus.

Um zu verstehen, worin das Problem besteht, muss das Modifizierungssymbol des letzten Fehlers abgerufen werden. Es verrät uns, dass keine Transaktion ausgewählt wurde. Das erledigt dieser Code:

for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--)
  {
   //if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue;        // forgot to select;
   ENUM_TRANS_TYPE type = TransactionType();
   if(type == TRANS_NOT_DEFINED)
   {
      ENUM_HEDGE_ERR error = GetHedgeError();
      printf("Error, transaction type not defined. Reason: " + EnumToString(error));
   }
  }

Anschließend erscheint folgende Meldung:

Error, transaction type not defined. Reason: HEDGE_ERR_TRANS_NOTSELECTED

Die Fehlerkennung soll uns klar machen, dass wir vergessen haben die Transaktion auszuwählen, bevor wir versucht haben, ihre Art abzurufen.

Eine Aufzählung aller möglichen Fehler bietet das Gerüst ENUM_HEDGE_ERR.


1.6. Ausführliche Analyse von Handelsoperationen und Ermittlung von Fehlern mit den Funktionen TotalActionsTask() und GetActionResult()

Zu den bei der Arbeit in einer HedgeTerminal-Umgebung auftretenden Fehlern können sich infolge des Aufrufs der Funktion SendTradeRequest() auch Handelsfehler gesellen. Der Umgang mit Fehlern dieser Art ist etwas komplizierter. Ein von der Funktion SendTradeRequest() ausgeführter Vorgang kann mehrere Handelsoperationen (Teilvorgänge) umfassen. So sind beispielsweise zwei Handelsoperationen auszuführen, um den Schlusskommentar einer aktiven, durch eine StopLoss-Grenze geschützten Position zu ändern:

  1. Die Aufhebung des die Stop-Grenze umsetzenden offenen Stop-Auftrages;
  2. Die Platzierung eines neuen offenen Stop-Auftrages an der Stelle des vorherigen, aber mit einem neuen Kommentar.

Wird der neue Stop-Auftrag ausgelöst, erscheint sein Kommentar, wie es sein muss, als der die Position schließende Schlusskommentar.

Aber ein Vorgang kann auch in Teilen ausgeführt werden. Angenommen, der offene Auftrag wird erfolgreich aufgehoben, aber die Platzierung des neuen Auftrages scheitert aus irgendeinem Grund. In diesem Fall bleibt die Position ohne StopLoss-Grenze. Damit das Expert-System diesen Fehler verarbeiten kann, muss es sich eines besonderen Vorgangsprotokolls bedienen und es durchsuchen, um den Teilvorgang ausfindig zu machen, der für den Fehler verantwortlich war.

Dazu dienen zwei Funktionen: TotalActionsTask() zur Ausgabe der Gesamtzahl der in den Vorgang eingehenden Handelsoperationen (Teilvorgänge) und GetActionResult() zum Bezug der Kennziffern der Teilvorgänge sowie zur Ausgabe ihrer Art und des Ergebnisses ihrer Ausführung. Da alle Handelsoperationen mithilfe der in MetaTrader 5 üblichen Hilfsmittel ausgeführt werden, entspricht das Ergebnis ihrer Ausführung dem Ausgabecode des Handelsservers.

Im Allgemeinen sieht der Suchalgorithmus für die Fehlerursachen wie folgt aus:

  1. Abrufen der Gesamtzahl der an unserem Vorgang beteiligten Teilvorgänge mithilfe der Funktion TotalActionsTask();
  2. Durchsuchen aller Teilvorgänge in einem for-Zyklus. Ermitteln der Art jedes Teilvorgangs sowie des Ergebnisses seiner Ausführung.

Nehmen wir an, der Grund für die Unmöglichkeit der Platzierung eines Stop-Auftrages mit einem neuen Kommentar war die zu große Nähe des Kurses für die Auftragsausführung zum aktuellen Kursniveau.

Der nachfolgende Code liefert ein Beispiel dafür, wie das Expert-System den Grund für diesen Fehler ermittelt haben könnte:

#include <Prototypes.mqh> 

ulong Magic=5760655; // MagicEx 1.3.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+ 
void OnTick()
  {
//detect active position
   for(int i=TransactionsTotal(MODE_HISTORY)-1; i>=0; i--)
     {
      if(!TransactionSelect(i,SELECT_BY_POS,MODE_HISTORY))continue;
      ENUM_TRANS_TYPE type=TransactionType();
      if(type==TRANS_NOT_DEFINED)
        {
         ENUM_HEDGE_ERR error=GetHedgeError();
         printf("Error, transaction not defined. Reason: "+EnumToString(error));
        }
      if(TransactionType()!=TRANS_HEDGE_POSITION)continue;
      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)continue;
      if(HedgePositionGetString(HEDGE_POSITION_SYMBOL) != Symbol())continue;
      HedgeTradeRequest request;
      request.action=REQUEST_MODIFY_COMMENT;
      request.exit_comment="My new comment";
      if(!SendTradeRequest(request)) // Is error?
        {
         for(uint action=0; action < TotalActionsTask(); action++)
           {
            ENUM_TARGET_TYPE typeAction;
            int retcode=0;
            GetActionResult(action, typeAction, retcode);
            printf("Action#" + (string)action + ": " + EnumToString(type) +(string)retcode);
           }
        }
     }
  }

Nach der Ausführung dieses Codes wird folgende Meldung ausgegeben:

Action #0 TARGET_DELETE_PENDING_ORDER 10009 (TRADE_RETCODE_PLACED)
Action #1 TARGET_SET_PENDING_ORDER 10015 (TRADE_RETCODE_INVALID_PRICE)

Tatsächlich wird beim Vergleich der Nummern mit den üblichen Modifizierungssymbolen des Ausgabecodes des Handelsservers deutlich, dass die Aufhebung des offenen Auftrages zwar erfolgreich verlaufen, die Platzierung des neuen dagegen gescheitert ist. Der Handelsserver hat die Fehlermeldung 10015 (unzulässiger Kurs) ausgegeben, was bedeuten kann, dass der aktuelle Kurs zu nahe an der Stop-Grenze liegt.

In diesem Wissen kann das Expert-System die Überwachung der Stop-Grenzen auf sich selbst umschalten. Dazu muss es die Position lediglich mithilfe derselben Funktion SendTradeRequest() eigenständig schließen.


1.7. Beobachten des Standes der Ausführung von Handelsvorgängen

Wie bereits beschrieben kann jeder Handelsvorgang aus einer beliebigen Anzahl von Teilvorgängen bestehen, die nacheinander ausgeführt werden.

Im asynchronen Modus des Zusammenwirkens kann ein Vorgang in mehreren Durchläufen des entsprechenden Codes ausgeführt werden. Es sind aber auch Fälle möglich, in denen die Ausführung des Vorgangs „sich aufhängt“. Aus all diesen Gründen muss das Expert-System die Überwachung der Ausführung des Vorgangs in der eigenen Hand haben. Beim Aufruf der Funktion HedgePositionGetInteger() mit dem Modifizierungssymbol HEDGE_POSITION_TASK_STATUS wird von ihr eine Aufzählung der Art ENUM_TASK_STATUS mit dem Stand der Ausführung des aktuellen Vorgangs zu der Position ausgegeben.

Wenn zum Beispiel nach Erteilung der Anweisung zum Schließen einer Position irgendetwas schief geht und die Position nicht geschlossen wurde, benötigt man den Stand der Ausführung des Vorgangs.

In dem nachfolgenden Beispiel wird ein Code vorgestellt, den ein asynchrones Expert-System zur Analyse des Standes der Ausführung eines aktuellen Vorgangs zu einer Position ausführen könnte:

ENUM_TASK_STATUS status=HedgePositionGetInteger(HEDGE_POSITION_TASK_STATUS);
switch(status)
  {
   case TASK_STATUS_COMPLETE:
      printf("Task complete!");
      break;
   case TASK_STATUS_EXECUTING:
      printf("Task executing. Waiting...");
      Sleep(200);
      break;
   case TASK_STATUS_FAILED:
      printf("Filed executing task. Print logs...");
      for(int i=0; i<TotalActionsTask(); i++)
        {
         ENUM_TARGET_TYPE type;
         uint retcode;
         GetActionResult(i,type,retcode);
         printf("#"+i+" "+EnumToString(type)+" "+retcode);
        }
      break;
   case TASK_STATUS_WAITING:
      printf("task will soon start.");
      break;
  }

Es sollte nicht vergessen werden, dass einige komplexe Vorgänge zu ihrer Ausführung mehrere Wiederholungen erfordern.

Im asynchronen Modus löst das Eintreten eines eine Veränderung in der Handelsumgebung signalisierenden Ereignisses eine neue Wiederholung aus. Somit werden alle Wiederholungen nach dem Eingang neuer Rückmeldungen seitens des Handelsservers unverzüglich eine nach der anderen ausgeführt. Im synchronen Modus erfolgt die Ausführung von Vorgängen etwas anders.

Damit die Anwender auch komplexe Vorgänge in einem Durchgang ausführen können, kommt im synchronen Modus ein Emulator für den synchronen Betrieb zum Einsatz. Er setzt bei seiner Arbeit auf zeitliche Verzögerungen. Wird beispielsweise die Ausführung eines Teilvorgangs gestartet, so gibt der Emulator die Ausführungsstrang nicht an das Expert-System weiter, er wartet damit stattdessen eine gewisse Zeit in der Hoffnung, dass sich die Handelsumgebung in dieser Zeit verändert. Danach liest der die Handelsumgebung erneut aus, und wenn er feststellt, dass sein Teilvorgang erfolgreich ausgeführt wurde, startet er die Ausführung des nächsten Teilvorgangs.

Durch diesen Aufbau wird die allgemeine Leistungsfähigkeit in gewissem Maße beeinträchtigt, da das Warten einige Zeit in Anspruch nimmt. Andererseits werden durch ihn selbst komplexe Vorgänge zu recht einfachen schlüssigen Operationen, die mit einem einzigen Funktionsaufruf ausgeführt werden. Deshalb muss das Vorgangsausführungsprotokoll im synchronen Modus eigentlich nie analysiert werden.


1.8. Ändern und Schließen gegenläufig gerichteter Positionen

Die Änderung und das Schließen gegenläufig gerichteter Position erfolgen mithilfe der Funktion SendTradeRequest(). An aktiven Positionen können insgesamt drei Operationen vorgenommen werden:

  1. sie können ganz oder teilweise geschlossen werden;
  2. ihre StopLoss- und TakeProfit-Grenzen können geändert werden;
  3. der Schlusskommentar zu ihnen kann geändert werden.

Historische Positionen aus dem Verlauf können nicht geändert werden. Ähnlich wie die Funktion OrderSend() in MetaTrader 5 verwendet die Funktion SendTradeRequest() für ihre Arbeit eine vorgefertigte Handelsanfrage in Form des Anfragegerüstes HedgeTraderRequest. In den entsprechenden Abschnitten der Dokumentation finden sich ausführliche Informationen zur Arbeitsweise der Funktion SendTradeRequest() und des HedgeTraderRequest-Gerüstes. Ein Beispiel zur Veranschaulichung der Änderung und Schließung von Positionen befindet sich in dem Abschnitt mit der Beschreibung des Expert-Systems Chaos II.


1.9. Einstellen der HedgeTerminal-Eigenschaften aus einem Expert-System heraus

HedgeTerminal besitzt eine Reihe von Eigenschaften, wie die regelmäßige Aktualisierung oder eine Mindestzahl von Sekunden als Wartezeit auf eine Rückmeldung des Servers.

All diese Eigenschaften werden in der Datei Settings.xml festgelegt. Wenn das Expert-System im Echtzeitmodus betrieben wird, liest die Bibliothek diese Eigenschaften aus der Datei aus und stellt die inneren Parameter entsprechend ein. Wird das Expert-System dagegen an einem Diagramm ausprobiert, kommt die Datei Settings.xml nicht zum Einsatz. Es können jedoch Situationen eintreten, in denen eine individuelle Anpassung dieser Eigenschaften aus dem Expert-System heraus erforderlich ist ganz gleich, ob es in einem Diagramm oder einem Strategieprüfprogramm läuft.

Für diese Fälle gibt es eine eigene Reihe von Funktionen HedgePropertySet… In der aktuellen Fassung ist lediglich ein Prototyp dieser Reihe verfügbar:

enum ENUM_HEDGE_PROP_INTEGER
{
   HEDGE_PROP_TIMEOUT,
};

bool HedgePropertySetInteger(ENUM_HEDGE_PROP_INTEGER property, int value)

Um zum Beispiel die Zeit einzustellen, für die die Bibliothek auf eine Rückmeldung des Servers warten soll, genügt es, Folgendes zu schreiben:

bool res = HedgePropertySetInteger(HEDGE_PROP_TIMEOUT, 30);

Nachdem diese Eigenschaft eingestellt ist, wird die gesperrte Position freigegeben, wenn die Rückmeldung des Servers bei Aufgabe einer asynchronen Anfrage nicht binnen 30 Sekunden eingeht.


1.10. Synchroner und asynchroner Betrieb

HedgeTerminal und seine Bibliothek (Api) führen die Handelsoperationen vollständig asynchron aus.

Das verlangt von den automatischen Handelssystemen allerdings eine komplexere Logik. Um diese Komplexität zu verbergen, ist in der HedgeTerminalApi ein besonderer Emulator für den synchronen Betrieb umgesetzt, der einem im gewöhnlichen synchronen Modus geschriebenen Expert-System das Zusammenwirken mit den asynchronen Algorithmen der HedgeTerminalApi ermöglicht. Dieses Zusammenwirken tritt immer dann zutage, wenn eine gegenläufig gerichtete Position mithilfe der Funktion SendTradeRequest() geändert und geschlossen wird. Diese Funktion ermöglicht die Ausführung von Handelsvorgängen sowohl im synchronen als auch im asynchronen Modus. Standardmäßig werden alle Handelsoperationen durch den Emulator für den synchronen Betrieb synchron ausgeführt. Wenn allerdings in einer Handelsanfrage (in dem HedgeTradeRequest-Gerüst) die Auszeichnung asynch_mode = true ausdrücklich angegeben ist, wird dieser Handelsvorgang asynchron ausgeführt.

Im asynchronen Modus erfolgt die Ausführung der Vorgänge unabhängig vom Hauptstrang. Das Zusammenspiel zwischen einem asynchronen Expert-System und den asynchronen HedgeTerminal-Algorithmen ist noch nicht vollständig ausgearbeitet.

Seinem Wesen nach ist der synchrone Emulator sehr schlicht. Er führt die Teilvorgänge der Reihe nach aus und wartet dann ein wenig, bis sich die Handelsumgebung in MetaTrader 5 ändert. Anhand der Analyse dieser Veränderungen bestimmt er den Stand der Ausführung des aktuellen Vorgangs. Wurde dieser erfolgreich ausgeführt, geht er zum nächsten über.

Der synchrone Emulator verursacht geringfügige Verzögerungen bei der Ausführung von Handelsanweisungen. Das ist dadurch bedingt, dass die Handelsumgebung in MetaTrader 5 einige Zeit benötigt, bis abgeschlossene Handelsoperationen in ihr wiedergegeben werden. Die Notwendigkeit des Zugangs zu dieser Umgebung hängt in erster Linie damit zusammen, dass die HedgeTerminalApi bei der Emulation eines synchronen Strangs keinen Zugriff auf Ereignisse hat, die der Verarbeitungsroutine OnTradeTransaction() zuzurechnen sind.

Die Problematik des Zusammenwirkens asynchroner Stränge miteinander sowie des eines asynchronen Strangs mit einem synchronen mithilfe der Emulation ist viel zu kompliziert und, es gibt keine erkennbaren Lösungen.


1.11. Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts

In folgendem Skript erfolgt die Durchsuchung aller verfügbaren Transaktionen in der Aufstellung der aktiven Transaktionen mithilfe der Funktion TransactionSelect().

Jede Transaktion wird in dieser Aufstellung ausgewählt. Handelt es sich bei ihr um eine Position, erfolgt der Zugriff auf einige ihrer Eigenschaften. Diese werden ausgedruckt. Außer den Eigenschaften der Position selbst werden auch die Eigenschaften der in sie eingegangenen Aufträge und Abschlüsse aufgenommen. Dazu werden zunächst der Auftrag und der Abschluss mithilfe der Funktionen HedgeOrderSelect() bzw. HedgeDealSelect() ausgewählt.

Alle den Positionen, Aufträge und Abschlüssen entnommenen Eigenschaften werden zusammengefasst und unter Verwendung der Systemfunktion printf in Form einer einzigen Zeile ausgedruckt.

//+------------------------------------------------------------------+
//|                                           sample_using_htapi.mq5 |
//|         Copyright 2014, Vasiliy Sokolov, Russia, St.-Petersburg. |
//|                              https://login.mql5.com/ru/users/c-4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, Vasiliy Sokolov."
#property link      "https://login.mql5.com/ru/users/c-4"
#property version   "1.00"

// Include prototypes function of HedgeTerminalAPI library.
#include <Prototypes.mqh> 

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+ 
void OnStart()
  {
   // Search all transaction in list transaction...
   for(int i=TransactionsTotal(); i>=0; i--)
     {
      if(!TransactionSelect(i,SELECT_BY_POS,MODE_TRADES))                           // Selecting from active transactions
        {
         ENUM_HEDGE_ERR error=GetHedgeError();                                      // Get reason if selecting has failed
         printf("Error selecting transaction # "+(string)i+". Reason: "+            // Print reason
                EnumToString(error));
         ResetHedgeError();                                                         // Reset error
         continue;                                                                  // Go to next transaction
        }
      // Only for hedge positions
      if(TransactionType()==TRANS_HEDGE_POSITION) 
        {
         // --- Position captions --- //
         ENUM_TRANS_DIRECTION direction=(ENUM_TRANS_DIRECTION)                      // Get direction caption
                              HedgePositionGetInteger(HEDGE_POSITION_DIRECTION);
         double price_entry = HedgeOrderGetDouble(HEDGE_ORDER_PRICE_EXECUTED);      // Get volume of positions
         string symbol = HedgePositionGetString(HEDGE_POSITION_SYMBOL);             // Get symbol of position
         // --- Order captions --- //
         if(!HedgeOrderSelect(ORDER_SELECTED_INIT))continue;                        // Selecting init order in position
         double slippage = HedgeOrderGetDouble(HEDGE_ORDER_SLIPPAGE);               // Get some slippage was
         uint deals_total = (uint)HedgeOrderGetInteger(HEDGE_ORDER_DEALS_TOTAL);    // Get deals total
         // --- Deals captions --- //
         double commissions=0.0;
         ulong deal_id=0;
         //Search all deals in list deals...
         for(uint d_index=0; d_index<deals_total; d_index++)                        
           {
            if(!HedgeDealSelect(d_index))continue;                                  // Selecting deal by its index
            deal_id = HedgeDealGetInteger(HEDGE_DEAL_ID);                           // Get deal id
            commissions += HedgeDealGetDouble(HEDGE_DEAL_COMMISSION);               // Count commissions
           }
         int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
         printf("Position #" + (string)i + ": DIR " + EnumToString(direction) +     // Print result line
         "; PRICE ENTRY " + DoubleToString(price_entry, digits) + 
         "; INIT SLIPPAGE " + DoubleToString(slippage, 2) + "; LAST DEAL ID " +
         (string)deal_id + "; COMMISSIONS SUM " + DoubleToString(commissions, 2));
        }
     }
  }

1.12. Beispiel für die Arbeit mit der Funktion SendTradeRequest() und dem HedgeTradeRequest-Gerüst am Beispiel des Expert-Systems Chaos II

Als Beispiel entwickeln wir ein automatisches Handelssystem auf der Grundlage der von Bill Williams in seinem Buch „Chaos handeln II“ (Trading Chaos. Zweite Auflage) vorgeschlagenen Handelstaktik.

Wir werden seinen Empfehlungen nicht vollständig Folge leisten, sondern sein Modell etwas vereinfachen, indem wir den Indikator Alligator sowie einige weitere Bedingungen aus unserer Strategie ausklammern. Die Wahl ausgerechnet dieser Strategie wurde von verschiedenen Gründen diktiert. Der Hauptgrund besteht darin, dass diese Strategie komplexe zusammengesetzte Taktiken zur Pflege von Positionen beinhaltet. Manchmal muss ein Teil des Umfangs einer Position geschlossen und die StopLoss-Grenze auf die Gewinnschwelle verschoben werden.

Wenn die Position zur Gewinnschwelle verschoben wurde, muss diese Stop-Grenze dem Kurs nachgezogen werden. Der zweite Grund ist die ausreichende Bekanntheit dieser Taktik sowie der Umstand, dass die für sie entwickelten Indikatoren zum Lieferumfang von MetaTrader 5 gehören. Dennoch ändern und vereinfachen wir die Regeln ein wenig, damit die Kompliziertheit der Logik des Expert-Systems selbst die vordringliche Aufgabe nicht verdeckt, die darin besteht, das Zusammenspiel des Expert-Systems mit der Bibliothek HedgeTerminalApi an einem Beispiel zu veranschaulichen. Die Logik des Expert-System ist derart ausgestaltet, dass sie den Großteil der Handelsfunktionen der HedgeTerminalApi nutzen wird. Das wird ein guter Test für die Bibliothek.

Als Ausgangspunkt dient uns ein gegenläufiger Balken. Ein aufwärts tendierender gegenläufiger Balken ist ein Balken, dessen Schlusskurs sich in seinem oberen Drittel befindet, und dessen Tiefstwert (Low) der niedrigste Wert der letzten N Balken ist. Bei einem abwärts tendierenden gegenläufigen Balken handelt es sich dagegen um einen Balken, dessen Schlusskurs sich in seinem unteren Drittel befindet, und dessen Höchstwert (High) der höchste Wert der letzten N Balken ist. Der Parameter N kann beim Start des Expert-Systems willkürlich festgelegt werden. Das ist der Unterschied zu der klassischen Chaos II-Strategie.

Nachdem der gegenläufige Balken ermittelt ist, werden zwei offene Aufträge (Pending Orders) platziert. Bei einem aufwärts tendierenden Balken werden sie oberhalb seines höchsten Wertes platziert, bei einem nach unten tendierenden knapp unterhalb seines Tiefstwerts. Wenn diese beiden Aufträge im Verlauf der OldPending-Balken nicht ansprechen, gilt das Signal als veraltet, und die Aufträge werden annulliert. Der Wert des Parameters OldPending sowie der Parameter N werden von dem Anwender vor der Ausführung des Expert-Systems in einem Diagramm festgelegt.

Nachdem sie angesprochen haben, verwandeln sich die Aufträge in zwei gegenläufig gerichtete Positionen einer Richtung. Das Expert-System unterscheidet sie anhand ihrer Nummer in dem jeweiligen Kommentar, # 1 bzw. # 2. Das ist keine besonders elegante Lösung, aber zur Veranschaulichung vollkommen ausreichend. Unmittelbar nach ihrer Auslösung wird für sie eine StopLoss-Grenze auf dem Niveau des Höchstwertes (bei einem abwärts tendierenden Balken) bzw. des Tiefstwertes (wenn der Balken nach oben tendiert) des jeweils gegenläufigen Balkens eingerichtet.

Die erste Position besitzt kurze Ziele. Für sie wird eine TakeProfit-Grenze festgelegt, deren Gewinn im Falle ihrer Auslösung gleich dem absoluten aus der Auslösung der StopLoss-Grenze resultierenden Verlust ist. Wenn zum Beispiel eine lange Position bei einem Kurs von 1,0000 eröffnet wurde und die StopLoss-Grenze zu ihr bei 0,9000 liegt, beträgt ihre TakeProfit-Grenze: 1,0000 + (1,0000 - 0,9000) = 1,1000. Der Ausstieg aus dieser Position würde sowohl bei Erreichen der einen wie der anderen Grenze erfolgen.

Die zweite Position ist eine langfristige (long). Ihre StopLoss-Grenze wird dem Kurs nachgezogen. Die Stop-Grenze folgt dem neu gebildeten Bill Williams-Fraktal. Bei einer langen Position folgt die Stop-Grenze den unteren Fraktalen, bei einer kurzen Position den oberen. Der Ausstieg aus der Position erfolgt nur bei Erreichen der StopLoss-Grenze.

Kommen wir zu dem Diagramm, das diese Strategie veranschaulicht:

Abb. 4. Darstellung der gegenläufig gerichteten Positionen des Expert-Systems Chaos II in einem Kursdiagramm

Abb. 4. Darstellung der gegenläufig gerichteten Positionen des Expert-Systems Chaos II in einem Kursdiagramm

Gegenläufige Balken sind rot eingerahmt. Der Zeitraum N ist gleich 2. Ausgewählt wurde der erfolgreichste Zeitpunkt für diese Strategie. Die kurzen Positionen werden als blaue gepunktete Linien dargestellt, die langen als grüne. Es zeigt sich, dass selbst in dieser recht einfachen Strategie Situationen auftreten, in denen lange und kurze Positionen gleichzeitig vorhanden sind. Achten Sie auf den Zeitraum vom 5. bis 8. Januar 2014.

Das ist der Wendepunkt für den absteigenden Trend des Kürzels AUDCAD. Am 4. Januar ging ein Signal von einem gegenläufigen aufwärts tendierenden Balken ein, und am 5. wurden bereits zwei lange Positionen eröffnet. Zu dieser Zeit bestanden noch drei kurze Positionen, deren Stop-Grenzen dem Trend nachgezogen wurden (die gepunktete rote Linie). Am 7. Januar sprachen dann die Stop-Grenzen für die kurzen Positionen an, sodass nur die langen Positionen im Markt blieben.

Bei einer Nettoposition wäre es äußerst schwierig, allem zu folgen, was vor sich geht, da die Nettomenge die Anzahl der von dem Expert-System tatsächlich gepflegten Positionen nicht berücksichtigen würde. HedgeTerminal erlaubt dem Expert-System, seine eigenen Einzelpositionen unabhängig von der aktuellen Nettoposition zu überwachen, wodurch es möglich ist solche Diagramme zu erhalten und vergleichbare Strategien zu entwickeln.

Es folgt der Code mit der Umsetzung dieser Strategie:

In ihm wurde bewusst auf den Einsatz der objektorientierten Programmierung (OOP) verzichtet, und er wurde so angepasst, dass auch Neulinge in Sachen Programmierung ihn nachvollziehen können.

//+------------------------------------------------------------------+
//|                                                       Chaos2.mq5 |
//|     Copyright 2014, Vasiliy Sokolov specially for HedgeTerminal. |
//|                                          St.-Petersburg, Russia. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, Vasiliy Sokolov."
#property link      "https://login.mql5.com/ru/users/c-4"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Prototypes.mqh>           // Include prototypes function of HedgeTerminalAPI library

//+------------------------------------------------------------------+
//| Input parameters.                                                |
//+------------------------------------------------------------------+
input uint N=2;                     // Period of extermum/minimum
input uint OldPending=3;            // Old pending

//+------------------------------------------------------------------+
//| Private variables of expert advisor.                             |
//+------------------------------------------------------------------+
ulong Magic = 2314;                 // Magic number of expert
datetime lastTime = 0;              // Remembered last time for function DetectNewBar
int hFractals = INVALID_HANDLE;     // Handle of indicator 'Fractals'. See: 'https://www.mql5.com/de/docs/indicators/ifractals'
//+------------------------------------------------------------------+
//| Type of bar by Bill Wiallams strategy.                           |
//+------------------------------------------------------------------+
enum ENUM_BAR_TYPE
  {
   BAR_TYPE_ORDINARY,               // Ordinary bar. 
   BAR_TYPE_BEARISH,                // This bar close in the upper third and it's minimum is lowest at N period
   BAR_TYPE_BULLISH,                // This bar close in the lower third and it's maximum is highest at N period
  };
//+------------------------------------------------------------------+
//| Type of Extremum.                                                |
//+------------------------------------------------------------------+
enum ENUM_TYPE_EXTREMUM
  {
   TYPE_EXTREMUM_HIGHEST,           // Extremum from highest prices
   TYPE_EXTREMUM_LOWEST             // Extremum from lowest prices
  };
//+------------------------------------------------------------------+
//| Type of position.                                                |
//+------------------------------------------------------------------+
enum ENUM_ENTRY_TYPE
  {
   ENTRY_BUY1,                      // Buy position with short target
   ENTRY_BUY2,                      // Buy position with long target
   ENTRY_SELL1,                     // Sell position with short target
   ENTRY_SELL2,                     // Sell position with long target
   ENTRY_BAD_COMMENT                // My position, but wrong comment
  };
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create indicator 'Fractals' ---//
   hFractals=iFractals(Symbol(),NULL);
   if(hFractals==INVALID_HANDLE)
      printf("Warning! Indicator 'Fractals' not does not create. Reason: "+
             (string)GetLastError());
//--- Corection magic by timeframe ---//
   int minPeriod=PeriodSeconds()/60;
   string strMagic=(string)Magic+(string)minPeriod;
   Magic=StringToInteger(strMagic);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Delete indicator 'Fractals' ---//
   if(hFractals!=INVALID_HANDLE)
      IndicatorRelease(hFractals);
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Run logic only open new bar. ---//
   int totals=SupportPositions();
   if(NewBarDetect()==true)
     {
      MqlRates rates[];
      CopyRates(Symbol(),NULL,1,1,rates);
      MqlRates prevBar=rates[0];
      //--- Set new pendings order ---//
      double closeRate=GetCloseRate(prevBar);
      if(closeRate<=30 && BarIsExtremum(1,N,TYPE_EXTREMUM_HIGHEST))
        {
         DeleteOldPendingOrders(0);
         SetNewPendingOrder(1,BAR_TYPE_BEARISH);
        }
      else if(closeRate>=70 && BarIsExtremum(1,N,TYPE_EXTREMUM_LOWEST))
        {
         DeleteOldPendingOrders(0);
         SetNewPendingOrder(1,BAR_TYPE_BULLISH);
        }
      DeleteOldPendingOrders(OldPending);
     }
//---
  }
//+------------------------------------------------------------------+
//| Analyze open positions and modify it if needed.                  |
//+------------------------------------------------------------------+
int SupportPositions()
  {
//---
   int count=0;
   //--- Analize active positions... ---//
   for(int i=0; i<TransactionsTotal(); i++) // Get total positions.
     {
      //--- Select main active positions ---//
      if(!TransactionSelect(i, SELECT_BY_POS, MODE_TRADES))continue;             // Select active transactions
      if(TransactionType() != TRANS_HEDGE_POSITION)continue;                     // Select hedge positions only
      if(HedgePositionGetInteger(HEDGE_POSITION_MAGIC) != Magic)                 // Select main positions by magic
      if(HedgePositionGetInteger(HEDGE_POSITION_STATE) == POSITION_STATE_FROZEN) // If position is frozen - continue
         continue;                                                               // Let's try to get access to positions later
      count++;
      //--- What position do we choose?... ---//
      ENUM_ENTRY_TYPE type=IdentifySelectPosition();
      bool modify=false;
      double sl = 0.0;
      double tp = 0.0;
      switch(type)
        {
         case ENTRY_BUY1:
         case ENTRY_SELL1:
           {
            //--- Check sl, tp levels and modify it if need. ---//
            double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL);
            sl=GetStopLossLevel();
            if(!DoubleEquals(sl,currentStop))
               modify=true;
            tp=GetTakeProfitLevel();
            double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
            double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
            //--- Close by take-profit if price more tp level
            bool isBuyTp=tp<bid && !DoubleEquals(tp,0.0) && type==ENTRY_BUY1;
            bool isSellTp=tp>ask && type==ENTRY_SELL1;
            if(isBuyTp || isSellTp)
              {
               HedgeTradeRequest request;
               request.action=REQUEST_CLOSE_POSITION;
               request.exit_comment="Close by TP from expert";
               request.close_type=CLOSE_AS_TAKE_PROFIT;
               if(!SendTradeRequest(request))
                 {
                  ENUM_HEDGE_ERR error=GetHedgeError();
                  string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : "";
                  printf("Close position by tp failed. Reason: "+EnumToString(error)+" "+logs);
                  if(error==HEDGE_ERR_TASK_FAILED)
                     PrintTaskLog();
                  ResetHedgeError();
                 }
               else break;
              }
            double currentTakeProfit=HedgePositionGetDouble(HEDGE_POSITION_TP);
            if(!DoubleEquals(tp,currentTakeProfit))
               modify=true;
            break;
           }
         case ENTRY_BUY2:
           {
            //--- Check sl level and set modify flag. ---//
            sl=GetStopLossLevel();
            double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL);
            if(sl>currentStop)
               modify=true;
            break;
           }
         case ENTRY_SELL2:
           {
            //--- Check sl level and set modify flag. ---//
            sl=GetStopLossLevel();
            double currentStop=HedgePositionGetDouble(HEDGE_POSITION_SL);
            bool usingSL=HedgePositionGetInteger(HEDGE_POSITION_USING_SL);
            if(sl<currentStop || !usingSL)
               modify=true;
            break;
           }
        }
      //--- if  need modify sl, tp levels - modify it. ---//
      if(modify)
        {
         HedgeTradeRequest request;
         request.action=REQUEST_MODIFY_SLTP;
         request.sl = sl;
         request.tp = tp;
         if(type==ENTRY_BUY1 || type==ENTRY_SELL1)
            request.exit_comment="Exit by T/P level";
         else
            request.exit_comment="Exit by trailing S/L";
         if(!SendTradeRequest(request))
           {
            ENUM_HEDGE_ERR error=GetHedgeError();
            string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : "";
            printf("Modify stop-loss or take-profit failed. Reason: "+EnumToString(error)+" "+logs);
            if(error==HEDGE_ERR_TASK_FAILED)
               PrintTaskLog();
            ResetHedgeError();
           }
         else break;
        }
     }
   return count;
//---
  }
//+------------------------------------------------------------------+
//| Return stop-loss level for selected position.                    |
//| RESULT                                                           |
//|   Stop-loss level                                                |
//+------------------------------------------------------------------+
double GetStopLossLevel()
  {
//---
   double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3;
   double fractals[];
   double sl=0.0;
   MqlRates ReversalBar;

   if(!LoadReversalBar(ReversalBar))
     {
      printf("Reversal bar load failed.");
      return sl;
     }
   //--- What position do we choose?... ---//
   switch(IdentifySelectPosition())
     {
      case ENTRY_SELL2:
        {
         if(HedgePositionGetInteger(HEDGE_POSITION_USING_SL))
           {
            sl=NormalizeDouble(HedgePositionGetDouble(HEDGE_POSITION_SL),Digits());
            CopyBuffer(hFractals,UPPER_LINE,ReversalBar.time,TimeCurrent(),fractals);
            for(int i=ArraySize(fractals)-4; i>=0; i--)
              {
               if(DoubleEquals(fractals[i],DBL_MAX))continue;
               if(DoubleEquals(fractals[i],sl))continue;
               if(fractals[i]<sl)
                 {
                  double price= SymbolInfoDouble(Symbol(),SYMBOL_ASK);
                  int ifreeze =(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_FREEZE_LEVEL);
                  double freeze=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*ifreeze;
                  if(fractals[i]>price+freeze)
                     sl=NormalizeDouble(fractals[i]+point,Digits());
                 }
              }
            break;
           }
        }
      case ENTRY_SELL1:
         sl=ReversalBar.high+point;
         break;
      case ENTRY_BUY2:
         if(HedgePositionGetInteger(HEDGE_POSITION_USING_SL))
           {
            sl=NormalizeDouble(HedgePositionGetDouble(HEDGE_POSITION_SL),Digits());
            CopyBuffer(hFractals,LOWER_LINE,ReversalBar.time,TimeCurrent(),fractals);
            for(int i=ArraySize(fractals)-4; i>=0; i--)
              {
               if(DoubleEquals(fractals[i],DBL_MAX))continue;
               if(DoubleEquals(fractals[i],sl))continue;
               if(fractals[i]>sl)
                 {
                  double price= SymbolInfoDouble(Symbol(),SYMBOL_BID);
                  int ifreeze =(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_FREEZE_LEVEL);
                  double freeze=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*ifreeze;
                  if(fractals[i]<price-freeze)
                     sl=NormalizeDouble(fractals[i]-point,Digits());
                 }
              }
            break;
           }
      case ENTRY_BUY1:
         sl=ReversalBar.low-point;
     }
   sl=NormalizeDouble(sl,Digits());
   return sl;
//---
  }
//+------------------------------------------------------------------+
//| Return Take-Profit level for selected position.                  |
//| RESULT                                                           |
//|   Take-profit level                                              |
//+------------------------------------------------------------------+
double GetTakeProfitLevel()
  {
//---
   double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3;
   ENUM_ENTRY_TYPE type=IdentifySelectPosition();
   double tp=0.0;
   if(type==ENTRY_BUY1 || type==ENTRY_SELL1)
     {
      if(!HedgePositionGetInteger(HEDGE_POSITION_USING_SL))
         return tp;
      double sl=HedgePositionGetDouble(HEDGE_POSITION_SL);
      double openPrice=HedgePositionGetDouble(HEDGE_POSITION_PRICE_OPEN);
      double deltaStopLoss=MathAbs(NormalizeDouble(openPrice-sl,Digits()));
      if(type==ENTRY_BUY1)
         tp=openPrice+deltaStopLoss;
      if(type==ENTRY_SELL1)
         tp=openPrice-deltaStopLoss;
      return tp;
     }
   else
      return 0.0;
//---
  }
//+------------------------------------------------------------------+
//| Identify what position type is select.                           |
//| RESULT                                                           |
//|   Return type position. See ENUM_ENTRY_TYPE                      |
//+------------------------------------------------------------------+
ENUM_ENTRY_TYPE IdentifySelectPosition()
  {
//---   
   string comment=HedgePositionGetString(HEDGE_POSITION_ENTRY_COMMENT);
   int pos=StringLen(comment)-2;
   string subStr=StringSubstr(comment,pos);
   ENUM_TRANS_DIRECTION posDir=(ENUM_TRANS_DIRECTION)HedgePositionGetInteger(HEDGE_POSITION_DIRECTION);
   if(subStr=="#0")
     {
      if(posDir==TRANS_LONG)
         return ENTRY_BUY1;
      if(posDir==TRANS_SHORT)
         return ENTRY_SELL1;
     }
   else if(subStr=="#1")
     {
      if(posDir==TRANS_LONG)
         return ENTRY_BUY2;
      if(posDir==TRANS_SHORT)
         return ENTRY_SELL2;
     }
   return ENTRY_BAD_COMMENT;
//---
  }
//+------------------------------------------------------------------+
//| Set pending orders under or over bar by index_bar.               |
//| INPUT PARAMETERS                                                 |
//|   index_bar - index of bar.                                      |
//|   barType - type of bar. See enum ENUM_BAR_TYPE.                 |
//| RESULT                                                           |
//|   True if new order successfully set, othewise false.            | 
//+------------------------------------------------------------------+
bool SetNewPendingOrder(int index_bar,ENUM_BAR_TYPE barType)
  {
//---
   MqlRates rates[1];
   CopyRates(Symbol(),NULL,index_bar,1,rates);
   MqlTradeRequest request={0};
   request.volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   double vol=request.volume;
   request.symbol = Symbol();
   request.action = TRADE_ACTION_PENDING;
   request.type_filling=ORDER_FILLING_FOK;
   request.type_time=ORDER_TIME_GTC;
   request.magic=Magic;
   double point=SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_SIZE)*3;
   string comment="";
   if(barType==BAR_TYPE_BEARISH)
     {
      request.price=rates[0].low-point;
      comment="Entry sell by bearish bar";
      request.type=ORDER_TYPE_SELL_STOP;
     }
   else if(barType==BAR_TYPE_BULLISH)
     {
      request.price=rates[0].high+point;
      comment="Entry buy by bullish bar";
      request.type=ORDER_TYPE_BUY_STOP;
     }
   MqlTradeResult result={0};
//--- Send pending order twice...
   for(int i=0; i<2; i++)
     {
      request.comment=comment+" #"+(string)i;       // Detect order by comment;
      if(!OrderSend(request,result))
        {
         printf("Trade error #"+(string)result.retcode+" "+
                result.comment);
         return false;
        }
     }
   return true;
//---
  }
//+------------------------------------------------------------------+
//| Delete old pending orders. If pending order set older that       |
//| n_bars ago pending orders will be removed.                       |
//| INPUT PARAMETERS                                                 |
//|   period - count bar.                                            |
//+------------------------------------------------------------------+
void DeleteOldPendingOrders(int n_bars)
  {
//---
   for(int i=0; i<OrdersTotal(); i++)
     {
      ulong ticket = OrderGetTicket(i);            // Get ticket of order by index.
      if(!OrderSelect(ticket))                     // Continue if not selected.
         continue;
      if(Magic!=OrderGetInteger(ORDER_MAGIC))      // Continue if magic is not main.
         continue;
      if(OrderGetString(ORDER_SYMBOL)!=Symbol())   // Continue if symbol is not main.
         continue;
      //--- Count time elipsed ---//
      datetime timeSetup=(datetime)OrderGetInteger(ORDER_TIME_SETUP);
      int secElapsed=(int)(TimeCurrent()-timeSetup);
      //--- delete old pending order ---//
      if(secElapsed>=PeriodSeconds() *n_bars)
        {
         MqlTradeRequest request={0};
         MqlTradeResult result={0};
         request.action= TRADE_ACTION_REMOVE;
         request.order = ticket;
         if(!OrderSend(request,result))
            printf("Delete pending order failed. Reason #"+(string)result.retcode+" "+result.comment);
        }
     }
//---
  }
//+------------------------------------------------------------------+
//| Detect new bar.                                                  |
//+------------------------------------------------------------------+
bool NewBarDetect(void)
  {
//---
   datetime timeArray[1];
   CopyTime(Symbol(),NULL,0,1,timeArray);
   if(lastTime!=timeArray[0])
     {
      lastTime=timeArray[0];
      return true;
     }
   return false;
//---
  }
//+------------------------------------------------------------------+
//| Get close rate. Type bar defined in trade chaos strategy         |
//| and equal enum 'ENUM_TYPE_BAR'.                                  |
//| INPUT PARAMETERS                                                 |
//|   index - index of bars series. for example:                     |
//|   '0' - is current bar. 1 - previous bar.                        |
//| RESULT                                                           |
//|   Type of ENUM_TYPE_BAR.                                         | 
//+------------------------------------------------------------------+
double GetCloseRate(const MqlRates &bar)
  {
//---
   double highLowDelta = bar.high-bar.low;      // Calculate diaposon bar.
   double lowCloseDelta = bar.close - bar.low;  // Calculate Close - Low delta.
   double percentClose=0.0;
   if(!DoubleEquals(lowCloseDelta, 0.0))                    // Division by zero protected.   
      percentClose = lowCloseDelta/highLowDelta*100.0;      // Calculate percent 'lowCloseDelta' of 'highLowDelta'.
   return percentClose;
//---
  }
//+------------------------------------------------------------------+
//| If bar by index is extremum - return true, otherwise             |
//| return false.                                                    |
//| INPUT PARAMETERS                                                 |
//|   index - index of bar.                                          |
//|   period - Number of bars prior to the extremum.                 |
//|   type - Type of extremum. See ENUM_TYPE_EXTREMUM TYPE enum.     |
//| RESULT                                                           |
//|   True - if bar is extremum, otherwise false.                    | 
//+------------------------------------------------------------------+
bool BarIsExtremum(const int index,const int period,ENUM_TYPE_EXTREMUM type)
  {
//--- Copy rates --- //
   MqlRates rates[];
   ArraySetAsSeries(rates,true);
   CopyRates(Symbol(),NULL,index,N+1,rates);
//--- Search extremum --- //
   for(int i=1; i<ArraySize(rates); i++)
     {
      //--- Reset comment if you want include volume analize. ---//
      //if(rates[0].tick_volume<rates[i].tick_volume)
      //   return false;
      if(type==TYPE_EXTREMUM_HIGHEST && 
         rates[0].high<rates[i].high)
         return false;
      if(type==TYPE_EXTREMUM_LOWEST && 
         rates[0].low>rates[i].low)
         return false;
     }
   return true;
//---
  }
//+------------------------------------------------------------------+
//| Print current error and reset it.                                |
//+------------------------------------------------------------------+  
void PrintTaskLog()
  {
//---
   uint totals=(uint)HedgePositionGetInteger(HEDGE_POSITION_ACTIONS_TOTAL);
   for(uint i = 0; i<totals; i++)
     {
      uint retcode=0;
      ENUM_TARGET_TYPE type;
      GetActionResult(i,type,retcode);
      printf("---> Action #"+(string)i+"; "+EnumToString(type)+"; RETCODE: "+(string)retcode);
     }
//---
  }
//+------------------------------------------------------------------+
//| Load reversal bar. The current position must be selected.        |
//| OUTPUT PARAMETERS                                                |
//|   bar - MqlRates bar.
//+------------------------------------------------------------------+  
bool LoadReversalBar(MqlRates &bar)
  {
//---
   datetime time=(datetime)(HedgePositionGetInteger(HEDGE_POSITION_ENTRY_TIME_SETUP_MSC)/1000+1);
   MqlRates rates[];
   ArraySetAsSeries(rates,true);
   CopyRates(Symbol(),NULL,time,2,rates);
   int size=ArraySize(rates);
   if(size==0)return false;
   bar=rates[size-1];
   return true;
//---   
  }
//+------------------------------------------------------------------+
//| Compares two double numbers.                                     |
//| RESULT                                                           |
//|   True if two double numbers equal, otherwise false.             |
//+------------------------------------------------------------------+
bool DoubleEquals(const double a,const double b)
  {
//---
   return(fabs(a-b)<=16*DBL_EPSILON*fmax(fabs(a),fabs(b)));
//---
  }

Wir stellen die Arbeitsweise dieses Codes in aller Kürze vor. Das Expert-System wird bei jeder Kursänderung (jedem Tick) aufgerufen. Es analysiert den vorhergehenden Balken mithilfe der Funktion BarIsExtremum() und platziert, wenn es sich um einen auf- oder abwärts tendierenden handelt, zwei offene Aufträge (mit der Funktion SetNewPendingOrder()). Nach der Auslösung verwandeln sich die offenen Aufträge in Positionen. Danach legt das Expert-System die StopLoss- und die TakeProfit-Grenze für sie fest.

Leider können diese Grenzen im Moment der Einrichtung der offenen Aufträge noch nicht festgelegt werden, da noch keine echten Positionen vorhanden sind. Die Festlegung dieser Grenzen obliegt der Funktion SupportPositions(). Um richtig zu funktionieren, muss sie wissen, für welche Position die TakeProfit-Grenze festgelegt und welche den neuen Fraktalen nachgezogen werden muss, Mit deren Ermittlung befasst sich die Funktion IdentifySelectPosition(). Sie analysiert zunächst den einleitenden Kommentar der Position, und legt, wenn dieser die Zeichenkombination #1 enthält, ein kurzes Ziel für sie fest; bei # 2 wird die Stop-Grenze nachgezogen.

Im Fall der Änderung oder der Schließung einer eröffneten gegenläufig gerichteten Position wird eine entsprechende Handelsanfrage erstellt und anschließend zur Ausführung an die Funktion SendTradeRequest() weitergegeben:

...
if(modify)
  {
   HedgeTradeRequest request;
   request.action=REQUEST_MODIFY_SLTP;
   request.sl = sl;
   request.tp = tp;
   if(type==ENTRY_BUY1 || type==ENTRY_SELL1)
      request.exit_comment="Exit by T/P level";
   else
      request.exit_comment="Exit by trailing S/L";
   if(!SendTradeRequest(request))
     {
      ENUM_HEDGE_ERR error=GetHedgeError();
      string logs=error==HEDGE_ERR_TASK_FAILED ? ". Print logs..." : "";
      printf("Modify stop-loss or take-profit failed. Reason: "+EnumToString(error)+" "+logs);
      if(error==HEDGE_ERR_TASK_FAILED)
         PrintTaskLog();
      ResetHedgeError();
     }
   else break;
  }
...

Achten Sie auf die Analyse der Fehler dieser Funktion.

Wenn die Weitergabe fehlgeschlagen ist, und die Funktion false ausgegeben hat, muss der Code des letzten Fehlers mithilfe der Funktion GetHedgeError() abgerufen werden. In manchen Fällen wird noch nicht einmal mit der Ausführung der Handelsanweisung begonnen. Denn, wenn zuvor keine Position ausgewählt oder die Anweisung nicht ordnungsgemäß angelegt worden ist, kann sie auch nicht ausgeführt werden.

Wenn die Anweisung nicht ausgeführt wurde, ist es sinnlos, das Protokoll ihrer Erfüllung zu analysieren, es reicht den Fehlercode abzurufen.

Wenn allerdings die Anfrage ordnungsgemäß erstellt wurde und die Anweisung aus anderen Gründen nicht ausgeführt worden ist, liegt der Fehler HEDGE_ERR_TASK_FAILED vor. In diesem Fall muss das Ausführungsprotokoll der Anweisung zur Analyse durchsucht werden. Das Durchsuchen des Protokolls ist Aufgabe der Sonderfunktion PrintTaskLog():

//+------------------------------------------------------------------+
//| Print current error and reset it.                                |
//+------------------------------------------------------------------+  
void PrintTaskLog()
  {
//---
   uint totals=(uint)HedgePositionGetInteger(HEDGE_POSITION_ACTIONS_TOTAL);
   for(uint i = 0; i<totals; i++)
     {
      uint retcode=0;
      ENUM_TARGET_TYPE type;
      GetActionResult(i,type,retcode);
      printf("---> Action #"+(string)i+"; "+EnumToString(type)+"; RETCODE: "+(string)retcode);
     }
//---
  }

Dank ihrer Meldungen ist es möglich, die Fehlerursache zu erkennen und zu beheben.

Zum Abschluss stellen wir eine Abbildung des Expert-Systems Chaos II und seiner Positionen in HedgeTerminal in Echtzeit vor. Das Expert-System wird in einem Minutendiagramm ausgeführt.

Abb. 5. Darstellung der gegenläufig gerichteten Positionen des Expert-Systems Chaos II in einem Kursdiagramm

Abb. 5. Darstellung der gegenläufig gerichteten Positionen des Expert-Systems Chaos II in einem Kursdiagramm

Offensichtlich kommen selbst die gegenläufig gerichteten Positionen eines einzigen Expert-Systems gut miteinander aus.


1.13. Über die so genannten „verdoppelten Kürzel“ und die Virtualisierung seitens des Maklers

Kurz nach dem Erscheinen von MetaTrader 5 haben einige Devisenmakler begonnen, so genannte duplizierte Kürzel anzubieten. Ihre Notierungen entsprechen denen der ursprünglichen Kürzel, aber sie enden in der Regel mit einem Postfix wie „_m“ oder „_1“. Sie wurden eingeführt, um Händlern das Halten gegenläufig gerichteter Positionen in scheinbar ein und demselben Kürzel zu ermöglichen.

Für den Algorithmen gestützten Devisenhandel mithilfe automatischer Handelssysteme sind diese Kürzel allerdings nutzlos. Und zwar deshalb: Stellen wir uns einmal vor, wir müssten das automatische Handels- oder Expert-System Chaos II ohne die Bibliothek der HedgeTerminalApi programmieren. An ihrer Stelle hätten wir einige duplizierte Kürzel. Wie könnten wir uns helfen? Angenommen alle Verkaufsoperationen würden in einem einzigen Währungspaar, etwa EURUSD, eröffnet, und alle Käufe in einem anderen, z. B. EURUSD_m1.

Aber was wäre, wenn im Moment der Eröffnung einer Position eines dieser Kürzel bereits von einem anderen automatischen Handelssystem oder einem Devisenhändler gehandelt würde? Selbst wenn diese Kürzel stets frei wären, wäre das Problem für unser Expert-System damit nicht gelöst, da es mehrere Positionen in derselben Richtung gleichzeitig halten kann.

Die Bildschirmaufnahme oben zeigt drei Verkaufspositionen, aber es könnten auch mehr sein. Die schützenden Stop-Grenzen fallen bei diesen Positionen unterschiedlich aus, weshalb diese nicht zu einer Nettoposition zusammengefasst werden können. Eine Lösung bestünde in der Eröffnung jeder Position in einem neuen duplizierten Kürzel. Aber dazu sind möglicherweise nicht genügend dieser fiktiven Währungspaare vorhanden, denn allein ein automatisches Handelssystem würde 6 duplizierte Kürzel benötigen (je drei in jede Handelsrichtung) Bei zwei automatischen Handelssystemen, die für unterschiedliche Zeiträume aufgerufen werden, wären also bereits 12 Kürzel erforderlich.

Diese Menge an duplizierten Kürzeln bietet kein einziger Devisenmakler, und dabei geht es hier nur um zwei automatische Handelssysteme. Aber selbst wenn diese Kürzel in unbegrenzter Zahl verfügbar, und sie alle stets frei wären, wäre eine komplizierte Zerlegung des Algorithmus erforderlich. Jedes Handelssystem müsste alle verfügbaren Kürzel nach Duplikaten und seinen eigenen Positionen durchsuchen. Dadurch würden mehr Probleme geschaffen als gelöst. 

Damit haben sich die Probleme mit den duplizierten Kürzeln allerdings noch nicht erschöpft. Es folgt eine kurze Übersicht über weitere bei ihrer Verwendung auftretende Schwierigkeiten:

  • Für jedes duplizierte Kürzel zahlt man aus eigener Tasche in Form dem eigenen Konto belasteter negativer Tauschgeschäfte (Swaps), denn diese sind beim Schließen oder teilweisen Schließen stets negativ, und zu einer solchen Überdeckung der Positionen kommt es auch dann, wenn zwei gegenläufig gerichtete Positionen in zwei unterschiedlichen Kürzeln gehalten werden.
  • Nicht alle Devisenmakler bieten duplizierte Kürzel an. Eine für Makler, die duplizierte Kürzel anbieten, entwickelte Strategie ist bei einem, der das nicht tut, zum Scheitern verurteilt. Wenn die Bezeichnungen der Kürzel voneinander abweichen, ist das eine weitere mögliche Problemquelle.
  • Das Anlegen eines duplizierten Kürzels ist nicht immer möglich. An streng geregelten transparenten Börsen ist jeder in einem Kürzel getätigte Abschluss eine finanzielle Urkunde. Auf solchen Märkten ist die Nettoposition de facto die Norm, weshalb das Anlegen eigener Kürzel dort schwer vorstellbar ist. An der Moskauer Börse wird zum Beispiel niemals ein Devisenmakler duplizierte Kürzel anbieten. Auf Märkten mit weniger strengen Regeln kann ein Devisenhändler seinen Kunden anbieten, was er will.
  • Beim Handel mithilfe automatischer Handelssysteme sind duplizierte Kürzel wenig hilfreich. Die Gründe dafür wurden bereits am Beispiel des Expert-Systems Chaos II dargelegt.

Ein dupliziertes Kürzel ist im Grunde eine Virtualisierung auf der Seite des Devisenmaklers. HedgeTerminal wendet sie auf die Kundenseite an.

In beiden Fällen kommt die Virtualisierung als solche zum Einsatz. Sie ändert die landläufige Vorstellung von den Pflichten eines Devisenhändlers. Bei der Virtualisierung kann sich eine Position in zwei verwandeln. Geschieht das auf der Seite des Kunden, ist es unproblematisch, da der Kunde sich vorstellen kann, was ihm gefällt. Wenn allerdings ein Devisenmakler diese Virtualisierung vornimmt, könnte sich bei den Regulierungs- und Zulassungsstellen Fragen danach ergeben, wie sich die gelieferten Informationen zu den Fakten verhalten. Die zweite Schwierigkeit besteht darin, dass man im Grunde zwei Bibliotheken (Apis) in einer haben muss: einen Satz Funktionen und Modifizierungssymbole für die Arbeit im Nettomodus, und einen für die Arbeit in gegenläufige Richtungen.

Zahlreiche Algorithmen gestützte Devisenhändler haben ihre eigenen Wege zur Bindung von Abschlüssen an eine einheitliche Position gefunden. Nicht wenige davon funktionieren gut, und es wurden ihnen sogar eigene Beiträge gewidmet. Nichtsdestotrotz handelt es sich bei der Virtualisierung von Positionen um einen komplizierteres Verfahren, als es scheinen mag. In HedgeTerminal nehmen die wie auch immer mit der Virtualisierung von Positionen verbundenen Algorithmen etwa 20.000 Zeilen Quellcode ein. Und dabei bildet der in HedgeTerminal umgesetzte Funktionsumfang lediglich den Grundstock. Das Anlegen einer vergleichbaren Menge von Code in einem Expert-System, nur um gegenläufig gerichtete Positionen zu begleiten, ist eine ungerechtfertigt ressourcenaufwendige Beschäftigung.


Kapitel 2. Anleitung zur Verwendung der HedgeTerminalApi

2.1. Funktionen zur Auswahl der Transaktionen

Die Funktion TransactionsTotal()

Die Funktion gibt die Gesamtzahl der Transaktionen in dem Transaktionsverzeichnis aus. Sie ist die Basisfunktion, die das Durchsuchen aller verfügbaren Transaktionen ordnet (siehe die Beispiele in den Abschnitten 1.4 und 1.11 des hier vorliegenden Beitrages).

int TransactionsTotal(ENUM_MODE_TRADES pool = MODE_TRADES);

Parameter

  • [in] pool=MODE_TRADES – weist die Kennung der Datenquelle für die Auswahl aus. Es kann sich um einen Wert aus der Aufzählung ENUM_MODE_TRADES handeln.

Ausgegebener Wert

Die Gesamtzahl der Transaktionen in dem Transaktionsverzeichnis.


Die Funktion TransactionType()

Die Funktion gibt die Art der ausgewählten Transaktion aus.

ENUM_TRANS_TYPE TransactionType(void);

Ausgegebener Wert

Die Art der ausgewählten Transaktion. Es kann sich um einen Wert aus der Aufzählung ENUM_TRANS_TYPE handeln.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion TransactionSelect()

Mithilfe dieser Funktion wird die zu bearbeitende Transaktion ausgewählt. Sie ermöglicht die Auswahl der Transaktion entweder anhand ihrer Kennziffer oder anhand ihres unverwechselbaren Bezeichners in der Aufstellung der Transaktionen.

bool TransactionSelect(int index,
     ENUM_MODE_SELECT select = SELECT_BY_POS,
     ENUM_MODE_TRADES pool=MODE_TRADES
     );

Parameter

  • [in] index – Die Kennziffer des Auftrages in der Auftragsaufstellung oder der unverwechselbare Bezeichner der Transaktion je nach Auswahlparameter.
  • [in] select=SELECT_BY_POS – Der Bezeichner der Art des Parameters „index“. Es kann sich um einen Wert aus der Aufzählung ENUM_MODE_SELECT handeln.
  • [in] pool=MODE_TRADES – weist die Kennung der Datenquelle für die Auswahl aus. Es kann sich um einen Wert aus der Aufzählung ENUM_MODE_TRADES handeln.

Ausgegebener Wert

„true“ bei erfolgreicher Auswahl einer Transaktion, „false“ bei einem Fehler. Um Angaben zu dem Fehler zu erhalten, ist die Funktion GetHedgeError() aufzurufen.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.

Hinweis

Wird eine Transaktion anhand ihrer Kennziffer ausgewählt, entspricht die Kompliziertheit der Operation O(1). Erfolgt die Auswahl anhand ihre unverwechselbaren Bezeichners, strebt die Kompliziertheit der Operation asymptotisch gegen O(log2(n)).


Die Funktion HedgeOrderSelect()

Die Funktion wählt einen der in die gegenläufig gerichtete Position eingehenden Aufträge aus. Die gegenläufig gerichtete Position, die den gewünschten Auftrag enthält, muss mithilfe der Funktion TransactionSelect () ausgewählt werden.

bool HedgeOrderSelect(ENUM_HEDGE_ORDER_SELECTED_TYPE type);

Parameter

Ausgegebener Wert

„true“ bei erfolgreicher Auswahl eines Auftrages, „false“ bei einem Fehler. Um Angaben zu dem Fehler zu erhalten, ist die Funktion GetHedgeError() aufzurufen.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion HedgeDealSelect()

Die Funktion wählt einen der Abschlüsse aus, durch die der Auftrag erfüllt worden ist. Der Auftrag, zu dem der gesuchte Abschluss gehört, muss im Vorfeld mithilfe der Funktion HedgeOrderSelect() ausgewählt worden sein.

bool HedgeDealSelect(int index);

Parameter

  • [in] index – Die Kennziffer des in der Aufstellung der an der Erfüllung des Auftrages beteiligten Abschlüsse ausgewählten Abschlusses. Die Gesamtzahl der zu einem Auftrag gehörenden Abschlüsse erfährt man durch Abruf der entsprechenden Auftragseigenschaft mithilfe der Funktion HedgeOrderGetInteger() unter Verwendung des Modifizierungssymbols ENUM_HEDGE_ORDER_PROP_INTEGER, das gleich dem Wert HEDGE_ORDER_DEALS_TOTAL ist, als Parameter.

Ausgegebener Wert

„true“ bei erfolgreicher Auswahl eines Abschlusses und „false“ bei einem Fehler. Um Angaben zu dem Fehler zu erhalten, ist die Funktion GetHedgeError() aufzurufen.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


2.2. Funktionen zum Bezug der Eigenschaften der gewählten Transaktion

Die Funktion HedgePositionGetInteger()

Die Funktion gibt eine Eigenschaft der ausgewählten gegenläufig gerichteten Position aus. Die Eigenschaft kann je nach Art der abgerufenen Eigenschaft die Art int, long, datetime oder bool aufweisen. Die gegenläufig gerichtete Position muss im Vorfeld mithilfe der Funktion TransactionSelect() ausgewählt worden sein.

ulong HedgePositionGetInteger(ENUM_HEDGE_POSITION_PROP_INTEGER property);

Parameter

  • [in] property – Der Bezeichner der Eigenschaft der gegenläufig gerichteten Position. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_DEAL_PROP_INTEGER handeln.

Ausgegebener Wert

Ein Wert der Art ulong. Damit mit ihm weitergearbeitet werden kann, muss er der Art der abgerufenen Eigenschaft sichtbar entsprechen.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion HedgePositionGetDouble()

Die Funktion gibt eine Eigenschaft der ausgewählten gegenläufig gerichteten Position aus. Die ausgegebene Eigenschaft ist von der Art double. Die Art der Eigenschaft wird mithilfe der Aufzählung ENUM_HEDGE_POSITION_PROP_DOUBLE ausgewiesen. Die gegenläufig gerichtete Position muss im Vorfeld mithilfe der Funktion TransactionSelect() ausgewählt worden sein.

ulong HedgePositionGetDouble(ENUM_HEDGE_POSITION_PROP_DOUBLE property);

Parameter

  • [in] property – Der Bezeichner der Eigenschaft der gegenläufig gerichteten Position. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_DEAL_PROP_DOUBLE handeln.

Ausgegebener Wert

Ein Wert der Art double.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion HedgePositionGetString()

Die Funktion gibt eine Eigenschaft der ausgewählten gegenläufig gerichteten Position aus. Die Eigenschaft ist von der Art string. Die Art der Eigenschaft wird mithilfe der Aufzählung ENUM_HEDGE_POSITION_PROP_STRING ausgewiesen. Die gegenläufig gerichtete Position muss im Vorfeld mithilfe der Funktion TransactionSelect() ausgewählt worden sein.

ulong HedgePositionGetString(ENUM_HEDGE_POSITION_PROP_STRING property);

Parameter

  • [in] property – Der Bezeichner der Eigenschaft der gegenläufig gerichteten Position. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_POSITION_PROP_STRING handeln.

Ausgegebener Wert

Ein Wert der Art string.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion HedgeOrderGetInteger()

Die Funktion gibt eine Eigenschaft des ausgewählten, in die gegenläufig gerichtete Position eingehenden Auftrages aus. Die Eigenschaft kann von der Art int, long, datetime oder bool sein. Die Art der Eigenschaft wird mithilfe der Aufzählung ENUM_HEDGE_ORDER_PROP_INTEGER ausgewiesen. Der Auftrag muss im Vorfeld mithilfe der Funktion HedgeOrderSelect() ausgewählt worden sein.

ulong HedgeOrderGetInteger(ENUM_HEDGE_ORDER_PROP_INTEGER property);

Parameter

  • [in] property – Der Bezeichner der Eigenschaft des in die gegenläufig gerichtete Position eingehenden Auftrages. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_ORDER_PROP_INTEGER handeln.

Ausgegebener Wert

Ein Wert der Art ulong. Damit mit ihm weitergearbeitet werden kann, muss er der Art der abgerufenen Eigenschaft sichtbar entsprechen.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion HedgeOrderGetDouble()

Die Funktion gibt eine Eigenschaft des ausgewählten, in die gegenläufig gerichtete Position eingehenden Auftrages aus. Die angeforderte Eigenschaft ist von der Art double. Die Art der Eigenschaft wird mithilfe der Aufzählung ENUM_HEDGE_ORDER_PROP_DOUBLE ausgewiesen. Der Auftrag muss im Vorfeld mithilfe der Funktion HedgeOrderSelect() ausgewählt worden sein.

double HedgeOrderGetDouble(ENUM_HEDGE_ORDER_PROP_DOUBLE property);

Parameter

  • [in] property – Der Bezeichner der Eigenschaft des in die gegenläufig gerichtete Position eingehenden Auftrages. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_ORDER_PROP_DOUBLE handeln.

Ausgegebener Wert

Ein Wert der Art double.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion HedgeDealGetInteger()

Die Funktion gibt eine Eigenschaft des ausgewählten, in den ausgeführten Auftrag eingehenden Abschlusses aus. Die Eigenschaft kann von der Art int, long, datetime oder bool sein. Die Art der Eigenschaft wird mithilfe der Aufzählung ENUM_HEDGE_DEAL_PROP_INTEGER ausgewiesen. Der Abschluss muss im Vorfeld mithilfe der Funktion HedgeDealSelect() ausgewählt worden sein.

ulong HedgeOrderGetInteger(ENUM_HEDGE_DEAL_PROP_INTEGER property);

Parameter

  • [in] property – Der Bezeichner der Eigenschaft des in den ausgeführten Auftrag eingehenden Abschlusses. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_DEAL_PROP_INTEGER handeln.

Ausgegebener Wert

Ein Wert der Art ulong. Damit mit ihm weitergearbeitet werden kann, muss er der Art der abgerufenen Eigenschaft sichtbar entsprechen.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion HedgeDealGetDouble()

Die Funktion gibt eine Eigenschaft des ausgewählten, in den ausgeführten Auftrag eingehenden Abschlusses aus. Die Eigenschaft kann von der Art double sein. Die Art der Eigenschaft wird mithilfe der Aufzählung ENUM_HEDGE_DEAL_PROP_DOUBLE ausgewiesen. Der Abschluss muss im Vorfeld mithilfe der Funktion HedgeDealSelect() ausgewählt worden sein.

ulong HedgeOrderGetDouble(ENUM_HEDGE_DEAL_PROP_DOUBLE property);

Parameter

  • [in] property – Der Bezeichner der Eigenschaft des in den ausgeführten Auftrag eingehenden Abschlusses. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_DEAL_PROP_DOUBLE handeln.

Ausgegebener Wert

Ein Wert der Art double.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


2.3. Die Funktionen zum Bezug und zur Einrichtung der HedgeTerminal-Eigenschaften aus Expert-Systemen

Die Funktion HedgePropertySetInteger()

Die Funktion legt eine der Eigenschaften von HedgeTerminal fest. Die Eigenschaft kann von der Art int, long, datetime oder bool sein. Die Art der Eigenschaft wird mithilfe der Aufzählung ENUM_HEDGE_PROP_INTEGER ausgewiesen.

bool HedgePropertySetInteger(ENUM_HEDGE_PROP_INTEGER property, long value);

Parameter

  • [in] property – Der Bezeichner der Eigenschaft, die für HedgeTerminal festgelegt werden muss. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_PROP_INTEGER handeln.

Ausgegebener Wert

Ein Wert der Art bool. Wurde die Eigenschaft erfolgreich festgelegt, gibt die Funktion „true“ aus, andernfalls „false“.

Anwendungsbeispiel

In dem Beispiel wird mithilfe der Funktion die Dauer der Sperrung einer Position bei Aufgabe einer asynchronen Anfrage festgelegt. Geht die Rückmeldung des Servers bei Aufgabe einer asynchronen Anfrage nicht binnen 30 Sekunden ein, wird die gesperrte Position wieder freigegeben.

void SetTimeOut()
  {
   bool res=HedgePropertySetInteger(HEDGE_PROP_TIMEOUT,30);
   if(res)
      printf("The property is set successfully");
   else
      printf("Property is not set");
  }

Die Funktion HedgePropertyGetInteger()

Die Funktion gibt eine der Eigenschaften von HedgeTerminal aus. Die Eigenschaft kann von der Art int, long, datetime oder bool sein. Die Art der Eigenschaft wird mithilfe der Aufzählung ENUM_HEDGE_PROP_INTEGER ausgewiesen.

long HedgePropertyGetInteger(ENUM_HEDGE_PROP_INTEGER property);

Parameter

  • [in] property – Der Bezeichner der aus HedgeTerminal abzurufenden Eigenschaft. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_PROP_INTEGER handeln.

Ausgegebener Wert

Ein Wert der Art long.

Anwendungsbeispiel

Die Funktion ruft die Dauer der Sperrung einer Position bei Aufgabe einer asynchronen Anfrage ab und gibt sie auf dem Ausgabegerät aus:

void GetTimeOut()
  {
   int seconds=HedgePropertyGetInteger(HEDGE_PROP_TIMEOUT);
   printf("Timeout is "+(string) seconds);
  }

2.4. Die Funktionen zum Bezug der Fehlercodes und deren Bearbeitung

Die Funktion GetHedgeError()

Die Funktion gibt den bei Abschluss der vorherigen Operation eingegangenen Fehlerbezeichner aus. Dieser entspricht der Aufzählung ENUM_HEDGE_ERR.

ENUM_HEDGE_ERR GetHedgeError(void);

Ausgegebener Wert

Der Fehlerbezeichner. Es kann sich um jede Wert aus der Aufzählung ENUM_HEDGE_ERR handeln.

Hinweis

Nachdem sie aufgerufen wurde, setzt die Funktion GetHedgeError() den Fehlerbezeichner nicht zurück. Zur seiner zwangsweisen Zurücksetzung ist die Funktion ResetHedgeError() zu verwenden.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion ResetHedgeError()

Die Funktion setzt den Bezeichner des letzten eingegangenen Fehlers zurück. Nach ihrem Aufruf ist der von der Funktion GetHedgeError() ausgegebene ENUM_HEDGE_ERR gleich HEDGE_ERR_NOT_ERROR.

void ResetHedgeError(void);

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.11 dieses Beitrages: Beispiel für die Bearbeitung der Eigenschaften gegenläufig gerichteter Positionen anhand eines Skripts.


Die Funktion TotalActionsTask()

Nach der Auswahl der Position mit der Funktion HedgePositionSelect() kann sie mithilfe der Funktion SendTradeRequest() verändert werden. Sie kann zum Beispiel geschlossen, oder ihr Schlusskommentar geändert werden. Diese Änderung wird von einem besonderen Handelsvorgang vorgenommen. Jeder Vorgang wiederum kann aus mehreren Handelsoperationen (Teilvorgängen) bestehen. Ein Vorgang kann auch fehlschlagen. In diesem Fall muss man das Ergebnis der Ausführung aller zum ihm gehörenden Teilvorgänge analysieren, um zu verstehen, bei welchem der Teilvorgänge der Fehler aufgetreten ist.

Die Funktion TotalActionTask() gibt die Gesamtzahl der zu dem letzten für die ausgewählte Position ausgeführten Handelsvorgang gehörenden Teilvorgänge aus. In Kenntnis ihrer Gesamtzahl können die Teilvorgänge anhand ihrer Kennziffern durchsucht, das Ergebnis ihrer Ausführung mithilfe der Funktion GetActionResult() analysiert und so herausgefunden werden, unter welchen Umständen es zu dem Fehler gekommen ist.

uint TotalActionsTask(void);

Ausgegebener Wert

Die Gesamtzahl der in den Vorgang eingehenden Teilvorgänge.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.6 dieses Beitrages: Ausführliche Analyse von Handelsoperationen und Ermittlung von Fehlern mit den Funktionen TotalActionsTask() und GetActionResult().


Die Funktion GetActionResult()

Die Funktion übernimmt die Kennziffer des in den Vorgang eingehenden Teilvorgangs (siehe TotalActionTask()). Sie gibt über Verknüpfungsparameter die Art des Teilvorgangs und das Ergebnis seiner Ausführung aus. Die Art des Teilvorgangs wird anhand der Aufzählung ENUM_TARGET_TYPE ermittelt. Das Ergebnis seiner Ausführung entspricht den Ausgabecodes des Handelsservers MetaTrader 5.

void GetActionResult(uint index, ENUM_TARGET_TYPE& target_type, uint& retcode);

Parameter

  • [in] index – Die Ordnungsnummer des Teilvorgangs in der Aufstellung der Teilvorgänge.
  • [out] target_type – Die Art des Teilvorgangs. Es kann sich um einen Wert aus der Aufzählung ENUM_TARGET_TYPE handeln.
  • [out] retcode – Der bei Ausführung des Teilvorgangs eingehende Ausgabecode des Handelsservers.

Anwendungsbeispiel

Siehe das Beispiel in Abschnitt 1.6 dieses Beitrages: Ausführliche Analyse von Handelsoperationen und Ermittlung von Fehlern mit den Funktionen TotalActionsTask() und GetActionResult().


2.5. Handel

Die Funktion SendTradeRequest()

Die Funktion sendet eine Anfrage zur Änderung der ausgewählten gegenläufig gerichteten Position an die HedgeTerminalApi. Das Ergebnis ihrer Ausführung ist eine der folgenden Operationen:

  1. Schließen der Position oder eines Teils ihres Umfangs;
  2. Änderung der StopLoss- und der TakeProfit-Grenze;
  3. Änderung des Schlusskommentars.

Die Art der Operation und ihre Eigenschaften werden in dem Gerüst HedgeTradeRequest angegeben, das per Verknüpfung als Parameter an die Funktion weitergegeben wird. Die gegenläufig gerichtete Position muss vor dem Aufruf der Funktion mithilfe der Funktion TransactionSelect() ausgewählt worden sein.

bool SendTradeRequest(HedgeTradeRequest& request);

Parameter

[in] request – Das Gerüst für die Anfrage zur Änderung der gegenläufig gerichteten Position. Zur Beschreibung des Gerüsts sowie zur Bedeutung der Felder s. die Beschreibung des Gerüsts HedgeTradeRequest.

Ausgegebener Wert

„true“, wenn die Anfrage zur Änderung der Position erfolgreich ausgeführt wurde. „false“, wenn nicht. Im Fall des Fehlschlagens der Ausführung der Anfrage sind zur Ermittlung des Fehlers und seiner Ursachen die Funktionen TotalActionsTask() und GetActionResult() zu verwenden.

Hinweis

Bei der asynchronen Aufgabe der Anfrage enthält die Ausgabeauszeichnung (Flag) „true“ wenn die Platzierung und der Start des Vorgangs erfolgreich waren. Allerdings ist zu bedenken, dass auch bei einem erfolgreichen Start des Vorgangs seine Ausführung nicht garantiert ist. Deshalb kann der Erfolg der Ausführung im asynchronen Modus nicht anhand der Ausgabeauszeichnung überwacht werden. Im synchronen Modus dagegen erfolgen der Start des Vorgangs und seine Ausführung in ein und demselben Strang, weswegen das Ergebnis der Ausführung einer Handelsanfrage in ihm mithilfe dieser Ausgabeauszeichnung überwacht werden kann.


Das Handelsanfragegerüst HedgeTradeRequest()

Die Schließung und Änderung gegenläufig gerichteter Positionen in HedgeTerminal erfolgt durch den Aufruf der Funktion SendTradeRequest() mit der Handelsanfrage als Argument. Die Anfrage wird durch das eigens dazu vordefinierte Gerüst HedgeTradeRequest wiedergegeben. Diese enthält alle zum Schließen oder Ändern der ausgewählten Position erforderlichen Felder:

struct HedgeTradeRequest
  {
   ENUM_REQUEST_TYPE action;             // type of action
   double            volume;             // volume of position
   ENUM_CLOSE_TYPE   close_type;         // Marker of closing order
   double            sl;                 // stop-loss level
   double            tp;                 // take-profit level
   string            exit_comment;       // outgoing comment
   uint              retcode;            // last retcode in executed operation
   bool              asynch_mode;        // true if the closure is performed asynchronously, otherwise false
   ulong             deviation;          // deviation in step price
                     HedgeTradeRequest() // default params
     {
      action=REQUEST_CLOSE_POSITION;
      asynch_mode=false;
      volume=0.0;
      sl = 0.0;
      tp = 0.0;
      retcode=0;
      deviation=3;
     }
  };

Beschreibung der Felder

FeldBeschreibung
 action Art der an der Position vorzunehmenden Operation. Es kann sich um jeden Wert aus der Aufzählung ENUM_REQUEST_TYPE handeln
 volume Zu schließender Umfang. Dieser kann geringer sein als der Gesamtumfang der aktuellen aktiven Position. Ist der Umfang gleich Null, wird die aktive Position vollständig geschlossen.
 sl Die für die aktive Position festzulegende StopLoss-Grenze.
 tp Die für die aktive Position festzulegende TakeProfit-Grenze.
 exit_comment  Der Schlusskommentar beim Schließen der aktiven Position.
 retcode Der Code des Ergebnisses der letzten ausgeführten Operation.
 asynch_mode „true“ bei asynchroner Aufgabe der Anfrage, andernfalls „false“.
 deviation Maximale Abweichung von dem Ausführungskurs.

2.6. Aufzählungen für die Arbeit mit den Funktionen zur Auswahl der Transaktionen

ENUM_TRANS_TYPE

Alle für die Analyse verfügbaren Transaktionen, gleich ob offene Aufträge (Pending Orders) oder gegenläufig gerichtete Positionen, befinden sich in den Aufstellungen der aktiven und der „historischen“ Transaktionen.

Die Aufzählung ENUM_TRANS_TYPE enthält die Art jeder ausgewählten Transaktion. Die Ausgabe der Aufzählung erfolgt mithilfe der Funktion TransactionType(). Die folgende Übersicht zeigt ihre Aufzählungsfelder und deren Beschreibung:

FeldBeschreibung
 TRANS_NOT_DEFINED Die Transaktion wurde nicht mithilfe der Funktion TransactionSelect() ausgewählt oder ihre Art wurde nicht bestimmt.
 TRANS_HEDGE_POSITION Die Transaktion ist eine gegenläufig gerichtete Position.
 TRANS_BROKERAGE_DEAL  Die Transaktion ist eine Makleroperation (ein Vorgang auf dem Konto). Zum Beispiel die Aufnahme von Mitteln oder die Aktualisierung des Kontos.
 TRANS_PENDING_ORDER Die Transaktion ist ein offener Auftrag (Pending Order).
 TRANS_SWAP_POS Die Transaktion ist ein auf die Nettoposition angerechnetes Tauschgeschäft.


ENUM_MODE_SELECT

Die Aufzählung bestimmt die Art des Parameters index, der in der Funktion TransactionSelect() festgelegt wird.

FeldBeschreibung
 SELECT_BY_POS In dem Parameter „index“ wird die Ordnungsnummer der Transaktion in der Aufstellung weitergegeben.
 SELECT_BY_TICKET In dem Parameter „index“ wird die Nummer des Kürzels (Tickets) weitergegeben.


ENUM_MODE_TRADES

Die Aufzählung bestimmt die Datenquelle, aus der die Transaktion mithilfe der Funktion TransactionSelect() ausgewählt wird.

FeldBeschreibung
 MODE_TRADES Die Transaktion wird aus den aktiven Transaktionen ausgewählt.
 MODE_HISTORY Die Transaktion wird aus den „historischen“ Transaktionen ausgewählt.

2.7. Aufzählungen für die Arbeit mit den Funktionen zum Bezug der Transaktionseigenschaften

Die Aufzählung ENUM_TRANS_DIRECTION

Jede Handelstransaktion, sei es ein Abschluss oder eine gegenläufig gerichtete Position, besitzt eine Marktrichtung.

Diese Marktrichtung wird durch die Aufzählung ENUM_TRANS_DIRECTION bestimmt. Ihre Felder und deren Beschreibung sehen aus wie folgt:

FeldBeschreibung
 TRANS_NDEF Die Transaktionsrichtung ist unbestimmt. Makleroperationen auf einem Konto haben beispielsweise keine Marktrichtung und weisen dieses Modifizierungssymbol auf.
 TRANS_LONG Gibt an, dass die Handelstransaktion (ein Auftrag oder eine gegenläufig gerichtete Position) zum Kauf (long) eröffnet wurde.
 TRANS_SHORT  Gibt an, dass die Handelstransaktion (ein Auftrag oder eine gegenläufig gerichtete Position) zum Verkauf (short) eröffnet wurde.


Die Aufzählung ENUM_HEDGE_POSITION_STATUS

Die Funktion enthält den Status der gegenläufig gerichteten Position.

FeldBeschreibung
 HEDGE_POSITION_ACTIVE  Eine aktive Position. Die aktiven Positionen werden unter der Registerkarte „Active“ des HedgeTerminal-Bedienfeldes abgebildet.
 HEDGE_POSITION_HISTORY  Eine „historische“ Position. Die historischen Positionen werden unter der Registerkarte „History“ des HedgeTerminal-Bedienfeldes abgebildet.


Die Aufzählung ENUM_HEDGE_POSITION_STATE

Die Funktion enthält den Stand der gegenläufig gerichteten Position.

FeldBeschreibung
 POSITION_STATE_ACTIVE Die ausgewählte Position ist aktiv und kann mithilfe der Handelsanfrage HedgeTradeRequest geändert werden.
 POSITION_STATE_FROZEN  Die ausgewählte Position ist gesperrt und kann nicht geändert werden. Bei Eingang dieses Modifizierungssymbols muss gewartet werden, bis die Position wieder freigegeben wird.


Die Aufzählung ENUM_HEDGE_POSITION_PROP_INTEGER

In der Aufzählung wird die Art der von der Funktion HedgePositionGetInteger() ausgegebenen Eigenschaft eingestellt.

FeldBeschreibung
 HEDGE_POSITION_ENTRY_TIME_SETUP_MSC Der Zeitpunkt der Erteilung des die gegenläufig gerichtete Position einleitenden Auftrags in Millisekunden seit dem 01. 01. 1970.
 HEDGE_POSITION_ENTRY_TIME_EXECUTED_MSC  Der Zeitpunkt der Ausführung des die gegenläufig gerichtete Position einleitenden Auftrags in Millisekunden seit dem 01. 01. 1970 (der Zeitpunkt der Positionseröffnung).
 HEDGE_POSITION_EXIT_TIME_SETUP_MSC Der Zeitpunkt der Erteilung des Auftrags zum Schließen der gegenläufig gerichteten Position in Millisekunden seit dem 01. 01. 1970.
 HEDGE_POSITION_EXIT_TIME_EXECUTED_MSC Der Zeitpunkt der Ausführung des die gegenläufig gerichtete Position schließenden Auftrags in Millisekunden seit dem 01. 01. 1970 (der Zeitpunkt der Positionsschließung).
 HEDGE_POSITION_TYPE Die Art der gegenläufig gerichteten Position. Sie entspricht der Art des einleitenden Auftrags. Enthält einen der Werte der systemischen Aufzählung ENUM_ORDER_TYPE.
 HEDGE_POSITION_DIRECTION Die Richtung der Position. Wird anhand der Aufzählung ENUM_TRANS_DIRECTION bestimmt.
 HEDGE_POSITION_MAGIC Die magische Zahl des Expert-Systems, zu dem die ausgewählte Position gehört. Der Wert Null zeigt an, dass die Position von Hand eröffnet wurde.
 HEDGE_POSITION_CLOSE_TYPE Das Markierungszeichen des Auftrages, der die Position schließt. Wird anhand der Aufzählung ENUM_CLOSE_TYPE bestimmt.
 HEDGE_POSITION_ID Der Positionsbezeichner. Er entspricht dem Bezeichner des einleitenden Auftrags.
 HEDGE_POSITION_ENTRY_ORDER_ID Der Bezeichner des einleitenden Auftrags.
 HEDGE_POSITION_EXIT_ORDER_ID Der Bezeichner des abschließenden Auftrages einer historischen Position.
 HEDGE_POSITION_STATUS Status der Position. Wird anhand der Aufzählung ENUM_HEDGE_POSITION_STATUS bestimmt.
 HEDGE_POSITION_STATE Stand der Position. Wird anhand der Aufzählung ENUM_HEDGE_POSITION_STATE bestimmt. 
 HEDGE_POSITION_USING_SL Die Auszeichnung für die Verwendung einer StopLoss-Grenze. Wird eine solche verwendet, gibt die Funktion HedgePositionGetInteger() „true“ aus, andernfalls „false“.
 HEDGE_POSITION_USING_TP Die Auszeichnung für die Verwendung einer TakeProfit-Grenze. Wird eine solche verwendet, gibt die Funktion HedgePositionGetInteger() „true“ aus, andernfalls „false“.
 HEDGE_POSITION_TASK_STATUS Der Status des Vorgangs, der für die ausgewählte Position ausgeführt wird. Die Position befindet sich möglicherweise im Prozess der Veränderung. Zur Verfolgung der Änderungen an der Position wird dieses Modifizierungssymbol verwendet. Der Status der Position wird mithilfe der Aufzählung ENUM_TASK_STATUS bestimmt.
 HEDGE_POSITION_ACTIONS_TOTAL Gibt die Gesamtzahl der zur Änderung dieser Position gestarteten Teilvorgänge aus.

 

Die Aufzählung ENUM_HEDGE_POSITION_PROP_DOUBLE

In der Aufzählung wird die Art der von der Funktion HedgePositionGetDouble() ausgegebenen Eigenschaft eingestellt.

FeldBeschreibung
 HEDGE_POSITION_VOLUME Die Art der gegenläufig gerichteten Position.
 HEDGE_POSITION_PRICE_OPEN Der gewichtete durchschnittliche Eröffnungskurs der Position.
 HEDGE_POSITION_PRICE_CLOSED Der gewichtete durchschnittliche Schlusskurs der Position.
 HEDGE_POSITION_PRICE_CURRENT Der aktuelle Kurs einer aktiven Position. Bei einer historischen Position gibt dieses Modifizierungssymbol den Schlusskurs der Position aus
 HEDGE_POSITION_SL Die Verlusteindämmungsgrenze StopLoss. Null, wenn diese Grenze nicht verwendet wird.
 HEDGE_POSITION_TP Die Gewinnsicherungsgrenze TakeProfit. Null, wenn diese Grenze nicht verwendet wird.
 HEDGE_POSITION_COMMISSION Der für die Position bezahlte Provisionsbetrag.
 HEDGE_POSITION_SLIPPAGE Der Umfang des Schwunds (Slippage) in Punkten.
 HEDGE_POSITION_PROFIT_CURRENCY  Der Umfang des Gewinns oder Verlusts der Position, ausgewiesen in der Einlagewährung.
 HEDGE_POSITION_PROFIT_POINTS Der Umfang des Gewinns oder Verlusts der Position, ausgewiesen in Punkten des Finanzinstrumentes zu dem die Position eröffnet wurde.

Hinweis

Der Umfang des Schwunds HEDGE_POSITION_SLIPPAGE wird als die Punktedifferenz zwischen dem Kurs des besten Einstiegsabschlusses und dem gewichteten durchschnittlichen Einstiegskurs für die Position berechnet.

 

Die Aufzählung ENUM_HEDGE_POSITION_PROP_STRING

In der Aufzählung wird die Art der von der Funktion HedgePositionGetString() ausgegebenen Eigenschaft eingestellt.

FeldBeschreibung
 HEDGE_POSITION_SYMBOL Das Kürzel, zu dem die aktuelle Position eröffnet wurde.
 HEDGE_POSITION_ENTRY_COMMENT Der Kommentar beim Einstieg in die Position
 HEDGE_POSITION_EXIT_COMMENT Der Kommentar beim Ausstieg aus der Position

 

Die Aufzählung ENUM_HEDGE_ORDER_STATUS

Die Aufzählung enthält die Auftragsart.

FeldBeschreibung
 HEDGE_ORDER_PENDING  Der Auftrag ist offen und in MetaTrader 5 unter der Registerkarte „Handel“ verfügbar.
 HEDGE_ORDER_HISTORY Der Auftrag ist „historisch“ und in MetaTrader 5 unter der Registerkarte „Auftragsarchiv“ verfügbar.

Die Aufzählung ENUM_HEDGE_ORDER_SELECTED_TYPE

Die Aufzählung bestimmt die Art des mithilfe der Funktion HedgeOrderSelect() ausgewählten Auftrages.

FeldWert
 ORDER_SELECTED_INIT Der die gegenläufig gerichtete Position eröffnende Auftrag.
 ORDER_SELECTED_CLOSED  Der die gegenläufig gerichtete Position schließende Auftrag.
 ORDER_SELECTED_SL Der die Funktion der StopLoss-Grenze ausführende Auftrag.


Die Aufzählung ENUM_HEDGE_ORDER_PROP_INTEGER

In der Aufzählung wird die Art der von der Funktion HedgePositionGetInteger() ausgegebenen Eigenschaft eingestellt.

FeldBeschreibung
 HEDGE_ORDER_ID Die unverwechselbare Auftragskennung.
 HEDGE_ORDER_STATUS Der Status des Auftrags. Es kann sich um einen Wert aus der Aufzählung ENUM_HEDGE_ORDER_STATUS handeln.
 HEDGE_ORDER_DEALS_TOTAL Die Gesamtzahl der den Auftrag ausführenden Abschlüsse. Bei offenen (pending) Aufträgen ist der Wert = 0.
 HEDGE_ORDER_TIME_SETUP_MSC Der Zeitpunkt der Platzierung des offenen Auftrages in Millisekunden seit dem 01. 01. 1970.
 HEDGE_ORDER_TIME_EXECUTED_MSC Der Zeitpunkt der Ausführung eines erfüllten Auftrages in Millisekunden seit dem 01. 01. 1970.
 HEDGE_ORDER_TIME_CANCELED_MSC Der Zeitpunkt der Annullierung eines erfüllten Auftrages in Millisekunden seit dem 01. 01. 1970.

Hinweis

Der Ausführungszeitpunkt HEDGE_ORDER_TIME_EXECUTED_MSC eines Auftrages entspricht dem Zeitpunkt des letzten zu ihm gehörenden Abschlusses. 


Die Aufzählung ENUM_HEDGE_ORDER_PROP_DOUBLE

In der Aufzählung wird die Art der von der Funktion HedgeOrderGetDouble() ausgegebenen Eigenschaft eingestellt.

FeldBeschreibung
 HEDGE_ORDER_VOLUME_SETUP Der Auftragsumfang bei seiner Erteilung (Platzierung).
 HEDGE_ORDER_VOLUME_EXECUTED Erfüllter Auftragsumfang. Bei einem offenen (pending) Auftrag ist der erfüllte Umfang gleich „0“.
 HEDGE_ORDER_VOLUME_REJECTED Nicht erfüllter Auftragsumfang. Er entspricht der Differenz zwischen dem ursprünglichen und dem erfüllten Umfang.
 HEDGE_ORDER_PRICE_SETUP Kurs bei Erteilung (Platzierung) des Auftrages
 HEDGE_ORDER_PRICE_EXECUTED Gewichteter Durchschnittskurs der Ausführung des Auftrags.
 HEDGE_ORDER_COMMISSION Umfang der an Makler gezahlten Provisionen für die Ausführung des Auftrags in der Einlagewährung.
 HEDGE_ORDER_SLIPPAGE Schwund (Slippage) des Auftrages.

Hinweis

Der Umfang des Schwunds HEDGE_ORDER_SLIPPAGE wird als die Punktedifferenz zwischen dem Kurs des besten ausgeführten Abschlusses und dem gewichteten durchschnittlichen Einstiegskurs für den Auftrag berechnet.


Die Aufzählung ENUM_HEDGE_DEAL_PROP_INTEGER

In der Aufzählung wird die Art der von der Funktion HedgeDealGetInteger() ausgegebenen Eigenschaft eingestellt.

FeldBeschreibung
 HEDGE_DEAL_ID Die unverwechselbare Abschlusskennung.
 HEDGE_DEAL_TIME_EXECUTED_MSC Der Zeitpunkt der Ausführung des Abschlusses in Millisekunden seit dem 01. 01. 1970.


Die Aufzählung ENUM_HEDGE_DEAL_PROP_DOUBLE

In der Aufzählung wird die Art der von der Funktion HedgeDealGetDouble() ausgegebenen Eigenschaft eingestellt.

FeldBeschreibung
 HEDGE_DEAL_VOLUME_EXECUTED Der Umfang des Abschlusses.
 HEDGE_DEAL_PRICE_EXECUTED Kurs bei Ausführung des Abschlusses.
 HEDGE_DEAL_COMMISSION Umfang der an Makler gezahlten Provisionen für die Ausführung des Abschlusses in der Einlagewährung.

 

2.8. Aufzählungen zur Einrichtung und zum Bezug der HedgeTerminal-Eigenschaften

Die Aufzählung ENUM_HEDGE_PROP_INTEGER

In der Aufzählung wird die Art der Eigenschaft festgelegt, die in HedgeTerminal abgerufen oder eingestellt werden soll.

FeldBeschreibung
 HEDGE_PROP_TIMEOUT Die Zeit in Sekunden, die der HedgeTerminal auf eine Rückmeldung des Servers wartet, bevor die Sperrung einer zu ändernden Position aufgehoben wird.

2.9. Aufzählungen für die Arbeit mit Funktionen zur Bearbeitung von Fehlercodes

Die Aufzählung ENUM_TASK_STATUS

Jede gegenläufig gerichtete Position kann sich im Prozess der Veränderung befinden. Für die Veränderung ist jeweils ein Handelsvorgang zuständig.

Jeder ausgelöste Handelsvorgang weist einen in der Aufzählung ENUM_TASK_STATUS bestimmten Ausführungsstatus auf. Ihre Felder und deren Beschreibung sehen folgendermaßen aus:

FeldBeschreibung
 TASK_STATUS_WAITING Kein aktueller Vorgang oder Vorgang im Wartestand.
 TASK_STATUS_EXECUTING Der Handelsvorgang zu der Position wird ausgeführt.
 TASK_STATUS_COMPLETE Der Handelsvorgang zu der Position wurde erfolgreich abgeschlossen.
 TASK_STATUS_FAILED Die Ausführung des Handelsvorgangs zu der Position wurde aufgrund eines Fehlers abgebrochen.

Die Aufzählung ENUM_HEDGE_ERR

Die Aufzählung enthält die Fehlerkennungen, die von der Funktion GetHedgeError() ausgegeben werden können.

FeldBeschreibung
 HEDGE_ERR_NOT_ERROR Es liegt kein Fehler vor.
 HEDGE_ERR_TASK_FAILED Der Handelsvorgang zu der Position konnte nicht ausgeführt werden.
 HEDGE_ERR_TRANS_NOTFIND Die Transaktion konnte nicht gefunden werden.
 HEDGE_ERR_WRONG_INDEX Unzulässige Kennziffer.
 HEDGE_ERR_WRONG_VOLUME Unzulässiger Umfang.
 HEDGE_ERR_TRANS_NOTSELECTED  Die Transaktion wurde nicht vorher mithilfe der Funktion TransactionSelect() ausgewählt.
 HEDGE_ERR_WRONG_PARAMETER Einer der weitergeleiteten Parameter ist unzulässig.
 HEDGE_ERR_POS_FROZEN Die gegenläufig gerichtete Position wird bereits geändert und ist für weitere Änderungen gesperrt. Warten Sie auf die Freigabe der Position.
 HEDGE_ERR_POS_NO_CHANGES Die Handelsanfrage enthält keine Änderungen.

 

Die Aufzählung ENUM_TARGET_TYPE

In der Aufzählung wird die Art des in der Funktion GetActionResult() gewählten Teilvorgangs bestimmt.

FeldBeschreibung
 TARGET_NDEF Es wurde kein Teilvorgang festgelegt.
 TARGET_CREATE_TASK Der Teilvorgang wird gerade angelegt. Diese Art kommt in der inneren Arbeitslogik der HedgeTerminalApi zum Einsatz.
 TARGET_DELETE_PENDING_ORDER Ein offener Auftrag wird gelöscht.
 TARGET_SET_PENDING_ORDER Ein offener Auftrag wird angelegt.
 TARGET_MODIFY_PENDING_ORDER  Der Kurs eines offenen Auftrags wird geändert
 TARGET_TRADE_BY_MARKET Die Handelsoperationen werden abgeschlossen.


2.10. Aufzählungen für die Arbeit mit Funktionen zur Bearbeitung von Fehlercodes

Die Aufzählung ENUM_REQUEST_TYPE

Die Aufzählung beschreibt die von HedgeTerminal an einer gegenläufig gerichteten Position auszuführende Operation.

FeldBeschreibung
 REQUEST_CLOSE_POSITION Schließen der Position. Wenn in dem Feld „volume“ des Gerüsts HedgeTradeRequest ein geringerer Umfang als der aktuelle angegeben ist, wird nur ein Teil der Position geschlossen. In diesem Fall entspricht der Teil der Position, der geschlossen wird, dem Wert in dem Feld „volume“.
 REQUEST_MODIFY_SLTP Festlegen oder Ändern der vorhandenen StopLoss- und TakeProfit-Grenzen.
 REQUEST_MODIFY_COMMENT Änderung des Ausstiegskommentars beim Schließen einer aktiven Position.


Die Aufzählung ENUM_CLOSE_TYPE

Die Aufzählung legt eine besondere Kennzeichnung für den die gegenläufig gerichtete Position schließenden Auftrag fest. Die Kennzeichnung weist den Grund für die Schließung der Position aus. Dabei kann es sich um einen der folgenden Gründe handeln:

  • das Erreichen der Verlust- oder StopLoss-Grenze durch die Position;
  • das Erreichen einer bestimmten Gewinn- oder TakeProfit-Grenze durch die Position;
  • das Schließen der Position durch den Markt. Es waren keine StopLoss- und TakeProfit-Grenze festgelegt oder sie wurden nicht erreicht.
FeldBeschreibung
 CLOSE_AS_MARKET Die Position wurde durch den Markt geschlossen. Es waren keine StopLoss- und TakeProfit-Grenze festgelegt oder sie wurden nicht erreicht.
 CLOSE_AS_STOP_LOSS Die Position wurde aufgrund des Erreichens der Verlusteindämmungsgrenze geschlossen.
 CLOSE_AS_TAKE_PROFIT  Die Position wurde aufgrund des Erreichens der Gewinnsicherungsgrenze geschlossen.

 

Kapitel 3. Grundlagen asynchroner Handelsoperationen

Das Thema asynchrone Handelsoperationen ist komplex und bedarf eines eigenen ausführlichen Beitrages. Aber angesichts des Umstandes, dass HedgeTerminal aktiv von asynchronen Handelsoperationen Gebrauch macht, ist es angemessen, die Grundlagen des Aufbaus automatischer Handelssysteme (Expert-Systeme), die sich dieser Art der Aufgabe von Handelsanweisungen bedienen, wenigstens kurz vorzustellen. Zudem gibt es derzeit de facto kein Material zu diesem Thema.

3.1. Aufbau und Muster der Platzierung einer synchronen Handelsanfrage

MetaTrader 5 verfügt über zwei Funktionen zur Übermittlung von Handelsanfragen an den Server:

Die Funktion OrderSend() empfängt die Anfrage in Form eines ausgefüllten MqlTradeRequest-Gerüsts und prüft grob die Richtigkeit der Ausfüllung. Verläuft die grobe Vorprüfung erfolgreich, leitet sie die Anfrage an den Server weiter und wartet auf das Ergebnis ihrer Ausführung, das sie anschließend über das Gerüst MqlTradeResult ebenso wie die Ausgabeauszeichnung an den benutzerdefinierten Strang zurückgibt. Schlägt die Vorprüfung fehl, gibt die Funktion einen negativen Wert aus.

Der Grund, aus dem die Anfrage nicht erfüllt werden konnte, wird ebenfalls in dem Gerüst MqlTradeResult ausgewiesen.

Es folgt eine grafische Darstellung der Ausführung des Strangs eines benutzerdefinierten MQL5-Programms unter Verwendung der Funktion OrderSend():

Abb. 6. Aufbau und Übermittlung einer synchronen Handelsanfrage

Abb. 6. Aufbau und Übermittlung einer synchronen Handelsanfrage.

Die Abbildung macht deutlich, dass der Strang des MQL5-Programms untrennbar mit dem allgemeinen Strang des Systems zur Übermittlung der Anfrage an den Server sowie zur Ausführung von Handelsoperationen an der Börse verbunden ist.

Deswegen ist es möglich, das tatsächliche Ergebnis der Ausführung der Handelsanfrage nach Abschluss der Funktion OrderSend() zu analysieren. Der benutzerdefinierte Strang ist durch rote Pfeile gekennzeichnet. Er wird praktisch sofort ausgeführt. Der Großteil der Zeit entfällt auf die Ausführung von Handelsoperationen an der Börse. Da beide Stränge verbunden sind, vergeht zwischen dem Beginn und dem Abschluss der Arbeit der Funktion OrderSend() eine beträchtliche Zeit. Dank des Umstandes, dass die Ausführung der Handelsoperationen in einem einzigen Strang erfolgt, kann das MQL5-Programm eine aufeinanderfolgende (sequenzielle) Logik aufweisen.


3.2. Aufbau und Muster der Platzierung einer asynchronen Handelsanfrage

Der Aufbau der Funktion OrderSendAsync() ist etwas anders. Wie OrderSend() empfängt auch sie die Handelsanfrage durch das Gerüst MqlTradeRequest und gibt eine Auszeichnung (Flag) mit dem Ergebnis ihrer Arbeit aus.

Anders als im ersten Beispiel wartet sie jedoch nicht auf die Ausführung der Anfrage durch den Handelsserver, sondern gibt lediglich die von dem Modul zur groben Vorprüfung der Werte der Handelsanfrage bezogenen Werte (Basic verification inside the terminal) aus. Es folgt eine Abbildung der Ausführung des benutzerdefinierten Strangs mithilfe der Funktion OrderSendAsync():

Abb. 7. Aufbau und Übermittlung einer asynchronen Handelsanfrage.

Abb. 7. Aufbau und Übermittlung einer asynchronen Handelsanfrage.

Nachdem die Handelsanfrage die Vorprüfung erfolgreich durchlaufen hat, wird sie parallel zum Hauptstrang an den Handelsserver weitergegeben. Für den Durchlauf der Handelsanfrage durch das Netz sowie für ihre Ausführung an der Börse wird wie im ersten Fall einige Zeit benötigt. Aber der benutzerdefinierte Strang selbst bezieht das Ergebnis von der Funktion OrderSendAsync() nahezu umgehend.

Die obige Abbildung verdeutlicht, dass die Funktion OrderSendAsync() tatsächlich einen neuen unabhängigen Parallelstrang bildet, der von dem Handelsserver ausgeführt wird, und dessen Ausführungsergebnis in die Funktion OnTradeTransaction() oder OnTrade() eingeht. Diese beiden Funktionen legen ihrerseits jeweils einen neuen benutzerdefinierten Strang an. In genau diesem neuen Strang muss das Ergebnis des Versands der Handelsanfrage verarbeitet werden. Dadurch wird die Logik des Expert-Systems erheblich komplizierter, da es bei der asynchronen Aufgabe eines Auftrages unmöglich ist, den Versand und die Prüfung der Anfrage in einem einheitlichen Strang zu organisieren. Der Code für den Versand und die Prüfung der Anfrage in der Funktion OnTick() kann beispielsweise nicht nacheinander angeordnet werden.

Wir programmieren zu Testzwecken ein schlichtes Expert-System, um das oben Gesagte zu veranschaulichen:

//+------------------------------------------------------------------+
//|                                                    AsynchExp.mq5 |
//|                           Copyright 2014, Vasiliy Sokolov (C-4). |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2014, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
input bool UsingAsynchMode=true;
bool sendFlag=false;
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(sendFlag)return;
   printf("Formation of order and send to the server...");
   MqlTradeRequest request={0};
   request.magic=12345;
   request.symbol = Symbol();
   request.volume = 0.1;
   request.type=ORDER_TYPE_BUY;
   request.comment= "asynch test";
   request.action = TRADE_ACTION_DEAL;
   request.type_filling=ORDER_FILLING_FOK;
   MqlTradeResult result;
   uint tiks= GetTickCount();
   bool res = false;
   if(UsingAsynchMode)
      res=OrderSendAsync(request,result);
   else
      res=OrderSend(request,result);
   uint delta=GetTickCount()-tiks;
   if(OrderSendAsync(request,result))
     {
      printf("The order has been successfully"+
             "sent to the server.");
     }
  else
     {
     printf("The order is not shipped."+
             " Reason: "+(string)result.retcode);
     }
   printf("Time to send a trade request: "+(string)delta);
   sendFlag=true;
//---
  }

Wir vergewissern uns, dass das Expert-System funktioniert und starten es mit dem Parameter UsingAsynchMode = false.

In diesem Fall eröffnet es eine lange Position im Umfang von 0,1 Posten. Die Handelsanfrage wird mithilfe der Funktion OrderSend() synchron ausgeführt. Nach seinem Start erhalten wir etwa folgendes Protokoll:

2014.11.06 17:49:28.442 AsynchExp (AUDCAD,H1)   Time to send a trade request: 94
2014.11.06 17:49:28.442 AsynchExp (AUDCAD,H1)   The order has been successfullysent to the server.
2014.11.06 17:49:28.345 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

Wie wir sehen, wurde die Handelsanfrage innerhalb von 94 Millisekunden ausgeführt. Diese Zeitangabe verrät uns, dass die Anfrage die Vorprüfung durchlaufen hat, an den Server weitergegeben wurde und danach ausgeführt worden ist.

Jetzt ändern wir den Code des Expert-Systems, indem wir den Umfang der Transaktion auf den höchstmöglichen Wert DBL_MAX ändern:

request.volume = DBL_MAX;

Offenkundig liegt dieser Wert jenseits der Grenzen des tatsächlichen Bereichs. Wir versuchen, diese Anfrage im synchronen Modus auszuführen:

2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1)   Time to send a trade request: 0
2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1)   The order is not shipped. Reason: 10014
2014.11.06 17:54:15.373 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

Der Versand der Anfrage ist fehlgeschlagen. Der Grund für den Fehlschlag ist der Fehler 10014 (Unzulässiger Auftragsumfang). Die Anfrage hat die Vorprüfung nicht bestanden und wurde nicht einmal an den Server weitergegeben, wovon die mit 0 Millisekunden angegebene Zeit für die Ausführung der Anfrage zeugt.

Wir ändern die Anfrage erneut. Diesmal stellen wir einen hinreichenden, wenn auch nicht extremen Umfang von 15 Posten ein. Für das 1.000-Dollarkonto, auf dem das Expert-System getestet wird, ist das zu viel. Eine solche Position kann in ihm nicht eröffnet werden.

Schauen wir, was die Funktion OrderSend() ausgibt:

2014.11.06 17:59:22.643 AsynchExp (AUDCAD,H1)   Time to send a trade request: 78
2014.11.06 17:59:22.643 AsynchExp (AUDCAD,H1)   The order is not shipped. Reason: 10019
2014.11.06 17:59:22.550 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

Jetzt ist der Fehler ein anderer: 10019 (Unzureichende Kontodeckung zur Ausführung der Anfrage). Beachten Sie die zur Ausführung der Anfrage benötigte Zeit, es sind 79 Millisekunden. Das zeugt davon, dass die Anfrage an den Server weitergeleitet wurde, und dieser dann Fehler mit dem Hinweis auf die mangelnde Kontodeckung ausgegeben hat.

Wir geben jetzt dieselbe Anfrage über 15 Posten mithilfe der Funktion OrderSendAsync() auf. Wie in dem Fall mit OrderSend() wird die Position nicht eröffnet, aber sehen wir uns das Protokoll genauer an:

2014.11.06 18:03:58.106 AsynchExp (AUDCAD,H1)   Time to send a trade request: 0
2014.11.06 18:03:58.106 AsynchExp (AUDCAD,H1)   The order has been successfully sent to the server.
2014.11.06 18:03:58.104 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

Das Protokoll besagt, dass kein Fehler vorliegt! Da der Handelsserver den Fehler 10019 festgestellt hat, ist dieser bei der asynchronen Auftragsaufgabe für den aktuellen Strang nicht verfügbar. Der Ausgabewert besagt lediglich, dass die Anfrage die Vorprüfung durchlaufen hat. Um den tatsächlichen Fehler 10019 zu erhalten, müssen wir das Ergebnis in einem neuen benutzerdefinierten Strang in der Systemfunktion OnTradeTransaction() analysieren, um die unser Expert-System erweitert werden muss:

void  OnTradeTransaction(const MqlTradeTransaction    &trans,
                         const MqlTradeRequest        &request,
                         const MqlTradeResult         &result)
  {
   uint delta = GetTickCount() - tiks;
   printf("Server answer: " + (string)result.retcode + "; Time: " + (string)delta);
  }

Wir starten das Expert-System ein weiteres Mal und erhalten folgendes Protokoll:

2014.11.06 18:17:00.943 AsynchExp (AUDCAD,H1)   Server answer: 10019; Time: 94
2014.11.06 18:17:00.854 AsynchExp (AUDCAD,H1)   Time to send a trade request: 0
2014.11.06 18:17:00.854 AsynchExp (AUDCAD,H1)   The order has been successfully sent to the server.
2014.11.06 18:17:00.851 AsynchExp (AUDCAD,H1)   Formation of order and send to the server...

Es erscheint die Fehlermeldung 10019, allerdings nicht sofort nach der Aufgabe, sondern bereits in einem in der Funktion OnTradeTransaction() aufgerufenen neuen benutzerdefinierten Strang.


3.3. Die Ausführungsgeschwindigkeit asynchroner Anfragen

Unter Devisenhändlern kursiert die irrige Annahme, dass die Ausführungsgeschwindigkeit bei einer asynchronen Anfrage gegen Null geht.

Sie beruht auf der Beobachtung, dass die Funktion OrderSendAsync() ihre Arbeit in aller Regel in weniger als einer Millisekunde ausführt. In Wirklichkeit muss die tatsächliche Ausführungszeit einer Handelstransaktion, wie oben gezeigt wurde, in dem Moment gemessen werden, in dem die Rückmeldung des Servers in einer der Funktionen OnTradeTransaction() oder OnTrade() empfangen wird. Durch diese Messung erhalten wir die echte Geschwindigkeit, die bei einer einzelnen Anweisung derjenigen ihrer synchronen Ausführung entspricht. Tatsächlich lassen sich die Vorteile bei der Ausführungszeit erst beim gleichzeitigen Versand mehrerer Transaktionen wahrnehmen. Zu den Situationen, die den gleichzeitigen Versand mehrerer Anfragen erfordern, gehören mindestens die folgenden drei:

  • Der zeitliche Abstand zwischen zwei aufeinanderfolgenden Anfragen ist zu kurz, um die Überprüfung des Ergebnisses der Ausführung des einen vor dem Versand der nächsten abzuschließen. Der Versand der nächsten Anfrage erfolgt in der Hoffnung, dass die vorherige ausgeführt wurde. Vergleichbare Taktiken werden beim Hochfrequenzhandel (HFT) verfolgt.
  • Es müssen gleichzeitig mehrere Positionen in mehreren Währungspaaren (Kürzeln) eröffnet werden. Zum Beispiel müssen bei Arbitragestrategien und zusammengesetzten künstlichen Positionen gleichzeitig Positionen in mehreren Währungspaaren (Kürzeln) zum jeweils aktuellen Kurs eröffnet werden. Die stufenweise Positionsbildung ist bei diesen Taktiken nicht erwünscht.
  • Der Strang ist so schnell wie möglich abzuschließen, und es muss auf die nächsten Ereignisse und Anweisungen des Anwenders gewartet werden. Bei mehrsträngigen infrastrukturellen Lösungen kommt diese Vorgabe zum Tragen. In erster Linie aus diesem Grund setzt HedgeTerminal auf asynchrone Anfragen. Würde HedgeTerminal den synchronen Anweisungsversand verwenden, würde er jedes Mal, wenn der Anwender eine Position schließt oder ändert, für 1 - 2 Sekunden „hängenbleiben“, und das wäre nicht hinnehmbar.

Es sei daran erinnert, dass bei der gleichzeitigen Aufgabe mehrerer Anfragen die Obergrenze für den Versand solcher Anfragen zu beachten ist.

Seit der Ausführung (build) 1010 beträgt dieser Grenzwert in MetaTrader 5 64 Transaktionen, von denen 4 den Anwendern vorbehalten sind, während die restlichen für Expert-Systeme zugänglich sind. Dieser Grenzwert wurde eingeführt, um beginnende Devisenhändler vor schwerwiegenden Fehlern in ihren Programmen zu bewahren und die Anzahl unerwünschter E-Mails (Spam) auf den Servern zu senken.

Das bedeutet, dass zum Beispiel in einem „for“-Zyklus durch den Aufruf der Funktion SendOrderAsync() mit einer entsprechenden Handelsanfrage bis zu 60 Handelsanweisungen gleichzeitig aufgegeben werden können. Nach dem Versand aller 60 Transaktionen ist der entsprechende Zwischenspeicher (Puffer) vollständig gefüllt. Wir müssen warten, bis der Server bestätigt, dass eine der Transaktionen von ihm verarbeitet worden ist.

Nach der Verarbeitung wird ihr Platz in dem Zwischenspeicher für die Transaktionen frei und kann mit einer neuen Handelsanweisung belegt werden. Sobald der Zwischenspeicher gefüllt ist, erfolgt die Freigabe von Platz für neue Transaktionen langsamer, da der Handelsserver zur Verarbeitung einer jeden Transaktion Zeit braucht, und das Ereignis TradeTransaction(), das den Beginn der Verarbeitung meldet, wird über das Netz übertragen, was zwangsläufig zu weiteren Verzögerungen führt.

Somit nimmt die zur Aufgabe von Anfragen benötigte Zeit im Verhältnis zu ihrer Anzahl nichtlinear zu. In der Tabelle unten wird die geschätzte Geschwindigkeit der Aufgabe von Anfragen im asynchronen Modus wiedergegeben. Die Tests werden mehrmals wiederholt, und die Ausführungsgeschwindigkeit wird als Durchschnittswert ausgewiesen.

Anzahl der AnfragenZeit, in Millisekunden
5050
100180
2002.100
5009.000
1.00023.000

Falls die Anzahl der Anfragen geringer als 60 ist, wartet das Skript nicht auf eine Rückmeldung des Servers, deshalb vergeht so wenig Zeit. Es dauert ungefähr so lange, wie beim Versand einer Anfrage. In der Tat muss zu der in der Tabelle ausgewiesenen Zeit der Auftragserteilung die durchschnittliche Zeit zur Ausführung einer Anfrage hinzugezählt werden, um die ungefähre tatsächliche Ausführungszeit zu ermitteln.

 

Kapitel 4. Grundlagen der mehrsträngigen Programmierung in einer MetaTrader 5-Umgebung

Wer in MQL programmiert, weiß, dass die Stränge (Threads) nicht unmittelbar aus einem MQL-Programm verwaltet werden können. Diese Beschränkung dient lediglich dem Schutz von Neulingen auf dem Gebiet der Programmierung, da die Verwendung von Strängen die Programmalgorithmen erheblich verkompliziert. Gleichwohl müssen in einigen Situationen zwei oder mehr Expert-Systeme miteinander zusammenarbeiten zum Beispiel, um gemeinsame Daten anzulegen und zu lesen.

Zu eben diesen Expert-Systemen gehört auch HedgeTerminal. Damit jedes die Bibliothek HedgeTerminalApi verwendende Expert-System weiß, was die jeweils anderen tun, organisiert HedgeTerminal den Datenaustausch über das mehrsträngige Lesen und Beschreiben der Datei ActivePositions.xml. Dabei handelt es sich um eine nicht ganz einfache und in MQL-Programmiererkreisen selten verwendete Lösung. Deshalb legen wir ein mehrsträngiges Expert-System an, dessen Algorithmus stark an den von HedgeTerminal erinnert. Das fördert das Verständnis der mehrsträngigen Programmierung und damit auch der Arbeitsweise von HedgeTerminal.


4.1. Mehrsträngige Programmierung am Beispiel der Notierungserfassung UnitedExchangeQuotes

Die Grundlagen der mehrsträngigen Programmierung untersuchen wir anhand eines konkreten Beispiels: wir programmieren die Erfassung der Notierungen unterschiedlicher Anbieter (Devisenmakler).

Die Idee sieht folgendermaßen aus: Angenommen wir haben 6 - 7 Makler, die Notierungen ein und desselben Kürzels anbieten. Natürlich können sich ihre Notierungen leicht voneinander unterscheiden. Die Analyse eben dieser Unterschiede eröffnet die Möglichkeit zur Verfolgung von Arbitragestrategien. Darüber hinaus hilft der Vergleich der Notierungen im Handelsverlauf bei der Ermittlung des besten und des am wenigsten erfolgversprechenden Anbieters. Wenn ein Makler ständig bessere Kurse bietet als die übrigen, könnte es geraten erscheinen, sich seiner für den Handel zu bedienen. Uns geht es hier nicht um den praktischen Wert der erzielten Ergebnisse, wir möchten lediglich den Mechanismus darstellen, mit dessen Hilfe diese Ergebnisse erzielt werden können.

Es folgt eine Bildschirmaufnahme des Expert-Systems, das wir am Ende dieses Abschnitts programmiert haben wollen:

Abb. 8. Erscheinungsbild der Notierungserfassung UnitedExchangeQuotes

Abb. 8. Erscheinungsbild der Notierungserfassung UnitedExchangeQuotes

Das Expert-System gibt die Ergebnisse in Form einer einfachen Tabelle mit vier Spalten und unbegrenzt vielen Zeilen wieder.

Jede Zeile steht für einen Makler, der Notierungen für das jeweilige Kürzel bietet (in unserem Fall für EURUSD). Ask und Bid sind der beste Angebots- bzw. Nachfragekurs des jeweiligen Maklers. Die Bildschirmaufnahme zeigt, dass die Kurse leicht voneinander abweichen. Die Differenz zwischen dem Angebot des aktuellen und eines anderen Maklers erscheint in der Spalte D-ASK (Delta Ask). Ganz ähnlich wird die Differenz zwischen den Nachfragekursen in der Spalte D-BID (Delta Bid) wiedergegeben. Als die Bildschirmaufnahme entstand, wies zum Beispiel Alpari Limited den gewinnbringendsten Angebotskurs (Ask) auf und die Bank VTB 24 den ungünstigsten.

MQL-Programme haben keinen Zugriff auf die Umgebung anderer MetaTrader-Ausgabegeräte (Terminals). Anders ausgedrückt, wenn ein Programm auf einem Gerät gestartet wurde, kann es keine Daten anderer Geräte empfangen. Allerdings können alle MQL-Programme über Dateien in den freigegebenen Verzeichnissen aller MetaTrader-Ausgabegeräte Daten miteinander austauschen. Zeichnet ein Programm eine Information, z. B. eine aktuelle Notierung, in einer freigegebenen Datei auf, dann kann ein MQL-Programm auf einem anderen Ausgabegerät diese lesen. Andere Möglichkeiten ohne Hinzuziehung fremder DLLs bietet MQL nicht. Deshalb bleiben wir bei diesem Verfahren.

Die größte Schwierigkeit besteht in der Organisation dieses Zugriffs. Denn das Expert-System muss zum einen die Notierungen anderer Anbieter lesen und andererseits die Notierung des Anbieters, auf dessen Ausgabegerät es gestartet wurde, in dieselbe Datei schreiben. Ein weiteres Problem ergibt sich aus dem Umstand, dass in dem Moment, in dem eine Notierung gelesen wird, ein anderes Expert-System genau diese Notierung überschreiben kann. Das Ergebnis dieser gleichzeitigen Arbeiten ist unvorhersehbar. Im besten Fall kommt es zu einer „Kollision“, durch die die Ausführung des Programms abgebrochen wird, schlimmstenfalls treten in Verbindung mit der Wiedergabe der Notierungen von Zeit zu Zeit kaum merkliche seltsame Fehler auf.

Um vergleichbaren Fehlern zu entgehen oder wenigstens die Wahrscheinlichkeit ihres Auftretens zu minimieren, stellen wir einen detaillierten Plan auf.

Zunächst speichern wir alle Informationen im Format XML. Dieses Format hat die älteren, recht plumpen INI-Dateien ersetzt. Außerdem ermöglicht das Format XML die flexible Entfaltung komplexer Datenstrukturen, etwa von Klassen, aus seinen Knoten. Danach befassen wir uns mit dem allgemeinen Algorithmus für das Lesen und Schreiben. Offenkundig gibt es zwei grundlegende Operationen: das Lesen und Schreiben von Daten. Während ein MQL-Programm liest oder schreibt, erhalten andere Programme keinen Zugriff auf die betreffende Datei. Somit sind Situationen ausgeschlossen, in denen ein Programm die Daten liest, und ein anderes sie ändert. Deshalb ist zu berücksichtigen, dass es nicht immer möglich ist, auf die Daten zuzugreifen.

Wir legen eine eigene Klasse CQuoteList mit den Algorithmen für den Zugriff auf die XML-Datei sowie den Daten zu allen Notierungen daraus an.

Eine der Funktionen dieser Klasse heißt TryGetHandle(), sie wird versuchen, Zugriff auf die Datei zu bekommen, und gibt im Erfolgsfall deren Bezeichner (Handle) aus. Und so wird sie umgesetzt:

int CQuoteList::TryGetHandle(void)
{
   int attempts = 10;
   int handle = INVALID_HANDLE;
   // We try to open 'attemps' times
   for(att = 0; att < attempts; att++)
   {
      handle = FileOpen("Quotes.xml", FILE_WRITE|FILE_READ|FILE_BIN|FILE_COMMON);
      if(handle == INVALID_HANDLE)
      {
         Sleep(15);
         continue;
      }
      break;
   }
   return handle;
}

Aus ihrem Aufbau wird ersichtlich, dass sie mehrere Versuche unternehmen wird, um die Datei im kombinierten Lese-/Schreib-Modus zu öffnen. Voreingestellt sind 10 Versuche.

Ist ein Versuch nicht von Erfolg gekrönt, wird die Funktion für 15 Millisekunden angehalten und solange weiter versuchen, die Datei zu öffnen, bis die Anzahl der Versuche erschöpft ist.

Sobald die Datei geöffnet ist, wird ihr Bezeichner an die Funktion LoadQuotes() weitergegeben. Eine vollständige Aufschlüsselung dieser Funktion sowie die Klasse CQuoteList selbst sind in der Anlage zu diesem Beitrag verfügbar. Daher werden wir den Inhalt der Funktion hier nicht vollständig aufführen, sondern beschränken uns auf die Darstellung der allgemeinen Abfolge ihrer Arbeitsgänge:

  1. TryGetHandle() öffnet die Datei zum Lesen und Beschreiben;
  2. Mithilfe der Bibliothek XML Parser wird das XML-Dokument in den Speicher des Expert-Systems geladen;
  3. Auf der Grundlage des geladenen XML-Dokuments wird ein neues Notierungsdatenfeld (Array) zur Speicherung der erforderlichen Informationen angelegt;
  4. In dem angelegten Datenfeld befindet sich die zu dem aktuellen Expert-System gehörende Notierung. Ihre Werte werden aktualisiert;
  5. Das Notierungsdatenfeld wird wieder in ein XML-Dokument umgewandelt. Der Inhalt der geöffneten XML-Datei wird durch dieses Dokument ersetzt;
  6. Die XML-Datei mit den Notierungen wird geschlossen.

Sichtlich leistet die Funktion LoadQuotes() sehr viel Arbeit, die jedoch in der Mehrzahl der Fälle weniger als 1 Millisekunde in Anspruch nimmt.

Das Lesen der Daten und ihre Aktualisierung mit abschließender Speicherung sind zu einem Block vereinigt. Das erfolgt insbesondere zu dem Zweck, zwischen den Lese- und Schreibvorgängen die Kontrolle über den Zugriff auf die Datei nicht zu verlieren.

Sobald die Notierungen geladen wurden und sich in der Klasse befinden, kann mit ihnen gearbeitet werden, wie mit anderen Daten in MetaTrader 5. Dazu wurde in der Klasse CQuotesList eine besondere im Stil von MetaTrader 5 geschriebene Programmschnittstelle umgesetzt.

Der Aufruf der Funktion und die Abbildung der Daten erfolgen in dem OnTick()-Block. Und so sieht sein Inhalt aus:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT))
      return;
   if(!QuotesList.LoadQuotes())    
      return;   
   PrintQuote quote = {0};
   Panel.DrawAccess(QuotesList.CountAccess());
   double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
   double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
   string brokerName = AccountInfoString(ACCOUNT_COMPANY);
   for(int i = 0; i < QuotesList.BrokersTotal(); i++)
   {
      if(!QuotesList.BrokerSelect(i))
         continue;
      if(!QuotesList.SymbolSelect(Symbol()))
         continue;
      quote.ask = QuotesList.QuoteInfoDouble(QUOTE_ASK);
      quote.bid = QuotesList.QuoteInfoDouble(QUOTE_BID);
      quote.delta_ask = ask - quote.ask;
      quote.delta_bid = quote.bid - bid;
      quote.broker_name = QuotesList.BrokerName();
      quote.index = i;
      Panel.DrawBroker(quote);
   }
  }

Bemerkenswert ist, dass der Programmcode in dem Beispiel ohne Änderungen sowohl in MetaTrader 4 als auch in MetaTrader 5 gleich funktioniert!

Es bestehen zwischen beiden Fassungen lediglich einige unbedeutende kosmetische Unterschiede in der Darstellung des Bedienfeldes. Das ist zweifelsfrei ein bemerkenswerter Umstand, der die Portierung von Code zwischen den Plattformen erleichtert.

Die Arbeit dieses Expert-Systems ist am besten und wirksamsten in Bewegung zu beobachten. Der folgende Film veranschaulicht die Arbeit des Expert-Systems an mehreren Konten.

 

Beim Lesen und Beschreiben der Datei hat da gewichtige Vorzüge, aber es gibt auch Nachteile.

Die wesentlichen Vorteile sind:

  1. Vielseitigkeit. Es können beliebige Daten gespeichert und geladen werden, sogar ganze Klassen;
  2. Vergleichsweise hohe Geschwindigkeit. Der gesamte Lese- und Überschreibvorgang dauert fast immer weniger als 1 Millisekunde, was vor dem Hintergrund der verhältnismäßig langsamen Handelsoperationen 80 bis 150 und manchmal sogar noch mehr Millisekunden vergehen, ein ziemlich guter Wert ist;
  3. Es beruht vollständig auf den üblichen Möglichkeiten der Programmiersprache MQL5 ohne Hinzuziehung externer DLL.

Die Hauptschwäche dieser Lösung ist die starke Beanspruchung des Datenspeichersystems. Wenn es nur um eine Notierung und zwei Makler geht, sind noch relativ wenige Überschreibevorgänge erforderlich, aber bei einem dicht gedrängten Notierungsstrang und einer großen Zahl von Maklern/Kürzeln vergrößert sich die Anzahl der Überschreibevorgänge exorbitant. In weniger als einer Stunde hat unser Anschauungsobjekt die Datei Quotes.xml mehr als 90.000 Mal überschrieben. Diese Statistik wird im oberen Teil des Bedienfeldes des Expert-Systems angezeigt: I/O Rewrite gibt die Gesamtzahl der an der Datei vollzogenen Überschreibevorgänge an, fps die Geschwindigkeit zwischen den beiden letzten von ihnen und Avrg ihre Durchschnittsgeschwindigkeit in der Sekunde.

Das Speichern der Dateien auf einem Halbleiterlaufwerk (SSD) oder einer klassischen Festplatte (HDD) beeinträchtigt deren Langlebigkeit. Daher empfiehlt sich für diese Art des Datenaustauschs die Verwendung eines virtuellen temporären Datenträgers im Arbeitsspeicher (RAM-Disk).

Im Unterschied zu dem vorgestellten Beispiel greift HedgeTerminal sparsam auf die Datei ActivePositions.xml zu und schreibt nur bedeutende Veränderungen an Positionen in sie ein, die über den allgemeinen Kontext nicht zugänglich sind. HedgeTerminal erfordert erheblich weniger Lese-/Schreibvorgänge auf als das Beispiel, weshalb bei seiner Verwendung keine besonderen Hilfsmittel in der Art von RAM-Disks erforderlich sind.


4.2. Anwendungsgebiete des mehrsträngigen Zusammenwirkens zwischen Expert-Systemen

Das Zusammenspiel zwischen unabhängigen MQL-Programmen in Echtzeit ist ein interessantes wenn auch schwieriges Thema. Wir haben es sehr komprimiert dargestellt, obwohl es einen eigenen Artikel verdienen würde. In der Mehrzahl der Fälle ist ein mehrsträngiges Zusammenwirken zwischen Expert-Systemen nicht erforderlich. Andererseits haben wir hier eine Aufstellung von Aufgaben und Varianten der Programme, für deren Aufbau ein solches Zusammenwirken erforderlich ist:

  • Abschlusskopierer. Jeder Abschlusskopierer bedingt den gleichzeitigen Start von mindestens zwei Expert-Systemen, von denen eines die Abschlüsse sendet, und das andere sie nachbildet. In diesem Fall muss das Senden und Empfangen der Abschlüsse durch mehrsträngiges Lesen/Beschreiben einer freigegebenen Datei organisiert werden;
  • Die Organisation des allgemeinen Datenaustausches zwischen automatischen Handelssystemen, globale Variablen. Die üblichen globalen Variablen in MetaTrader 5 sind für die Expert-Systeme nur auf einem einzigen Ausgabegerät verfügbar. Eine auf einem Ausgabegerät (Terminal) festgelegte globale Variable ist keinem anderen zugänglich. Mithilfe freigegebener Daten kann man dagegen komplexe globale Variablen anlegen, die für alle Ausgabegeräte (Terminals), selbst in unterschiedlichen Fassungen, zugänglich sind.
  • Arbitragestrategien. Analysatoren der Notierungen von Liquiditätsanbietern. Wenn der Unterschied zwischen den Kursen unterschiedlicher Anbieter erheblich ist, ist es möglich, gewinnbringende Arbitragestrategien zu entwickeln. Mithilfe von Notierungsanalysatoren lassen sich zudem eine Kursstatistik anlegen und der beste Liquiditätsanbieter ermitteln.


Beschreibung der Dateien im Anhang

Es folgen kurze Darstellungen der Dateien im Anhang zu diesem Beitrag sowie ihrer Einbindung (Kompilierung).

Prototypes.mqh ist eine Datei mit den Beschreibungen der Funktionen der Bibliothek HedgeTerminalApi. Diese Datei enthält die Beschreibung sowie Prototypen der Funktionen aus der Bibliothek HedgeTerminalApi. Dank ihr „weiß“ unser Expert-System, welche Funktionen und Modifizierungssymbole in der Bibliothek verfügbar sind, wie diese Funktionen richtig aufgerufen werden, und welche Werte sie ausgeben.

Diese Datei muss in dem Ordner C:\Program Files\MetaTrader 5\MQL5\Include abgelegt werden, wobei C:\Program Files\MetaTrader 5\ den Pfad zu der MetaTrader 5-Instanz auf Ihrem Ausgabegerät (Terminal) bezeichnet. Sobald die Datei in das erforderliche Verzeichnis kopiert wurde, kann in Ihrem MQL-Programm eine Verknüpfung (ein Link) zu ihm angelegt werden. Das ist jedes Mal dann erforderlich, wenn die Bibliothek HedgeTerminalApi verwendet werden muss. Um die Datei Prototypes.mqh einzubinden, muss der Programmcode um eine besondere Anweisung zu ihrer Einbindung erweitert werden:

#include <Prototypes.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   //...
   // Here is the content of your program.
   //...
  }

Diese Anweisung ist in dem Beispiel oben gelb hervorgehoben und lautet #include <Prototypes.mqh>. Jetzt kann das oben aufgeführte Skript mit den Funktionen der Bibliothek verknüpft werden und deren Funktionsumfang für seine Arbeit nutzen.

Es ist zu bedenken, dass die Datei mit den Prototypen im Verlauf der Weiterentwicklung der Bibliothek HedgeTerminalApi kleinen Änderungen unterworfen sein kann. Häufig ist es bei Erscheinen aktualisierter Fassungen der Bibliothek erforderlich, auch die Prototypendatei zu aktualisieren, die die erfolgten Änderungen beschreibt. Haben Sie bitte Verständnis für diese Unannehmlichkeit. In jedem Fall kann die aktuelle Fassung der Prototypendatei jederzeit manuell aus der Bibliothek installiert (eine Beschreibung des Installationsvorgangs finden Sie in Abschnitt 1.1), oder aus dem Anhang zu diesem Beitrag (regelmäßige Aktualisierungen sind vorgesehen) heruntergeladen werden.

Chaos2.mqh ist der Quellcode des Expert-Systems Chaos II. Eine Beschreibung seiner Arbeitsweise bietet Abschnitt 1.12: Beispiel für die Arbeit mit der Funktion SendTradeRequest() und dem HedgeTradeRequest-Gerüst am Beispiel des Expert-Systems Chaos II. Um diesen Code erfolgreich einzubinden, muss sich die Datei Prototypes.mqh mit den Prototypen der Funktionen in dem entsprechenden Verzeichnis \Include befinden, und die Bibliothek HedgeTerminalApi muss unter folgender Adresse abgelegt werden: C:\Program Files\MetaTrader 5\MQL5\Market\hedgeterminalapi.ex5. Wobei C:\Program Files\MetaTrader 5\ das Verzeichnis (des Dateiordners auf dem Ausgabegerät/Terminal) bezeichnet, in dem Ihre Instanz von MetaTrader 5 installiert ist.

Bei dem Quellcode UnitedExchangeQuotes handelt es sich um eine besondere gepackte Datei (unitedexchangequotes.zip) mit dem in Kapitel 4 ausführlich vorgestellten Projekt gleichen Namens: Grundlagen der mehrsträngigen Programmierung in einer MetaTrader 5-Umgebung. Darin befinden sich folgende Dateien:

  • UnitedExchangeQuotes.mq5 ist die zentrale Datei des gleichnamigen Expert-Systems. Sie muss in dem Ordner für automatische Handelssysteme dieser Art abgelegt werden: \MetaTrader 5\MQL5\Experts. Diese Datei muss unverzüglich in dem Verarbeitungsprogramm MetaEditor unverzüglich in Computercode übersetzt werden.
  • MultiThreadXML.mqh enthält in der Hauptsache die Algorithmen für den mehrsträngigen Zugriff auf die XML-Datei. Sie organisiert den Informationsaustausch zwischen den voneinander unabhängigen Strängen. Sie gehört in das Verzeichnis \MetaTrader 5\MQL5\Include. Die Algorithmen in dieser Datei beruhen auf einer besonderen von ya-sha entwickelten und in der Codedatenbank CodeBase verfügbaren Bibliothek. Für die Verwendung im mehrsträngigen Modus müssen sie allerdings leicht angepasst werden. Eine entsprechend überarbeitete Fassung befindet sich ebenfalls in besagtem Anhang. Sie umfasst folgende Dateien:
    • XmlBase.mqh;
    • XmlDocument.mqh;
    • XmlAttribute.mqh;
    • XmlElement.mqh.
    Diese Dateien gehören ebenfalls in den Ordner \Include.
  • Panel.mqh enthält die in dem Beispiel vorgestellte Klasse für das Bedienfeld. Sie muss in demselben Verzeichnis untergebracht werden wie die Datei UnitedEchangesQuotes.mqh, also in dem Ordner \Experts.

Alle Dateien in dieser gepackten Datei enthalten relative Pfade. Die Datei UnitedExchangeQuotes.mq5 zum Beispiel befindet sich in dem Ordner \MQL5\Experts. Das bedeutet, dass sie in einem Unterverzeichnis mit derselben Bezeichnung in dem Datenordner der MetaTrader 5-Instanz auf dem Ausgabegerät (Terminal) untergebracht werden muss, etwa in C:\Program Files\MetaTrader 5\MQL5\Experts\UnitedExchangeQuotes.mq5.


Fazit

Wir haben uns ausführlich mit den Einzelheiten der Arbeit mit der Programmschnittstelle HedgeTerminal befasst.

Es wurde gezeigt, dass die Grundlagen dieser Bibliothek denjenigen der Api-Bibliothek in MetaTrader 4 sehr ähnlich sind. Genau wie in MetaTrader 4 muss sie vor dem Beginn der Bearbeitung einer Transaktion (das entspricht dem Begriff „Auftrag“ in MetaTrader 4) zunächst mithilfe der Funktion TransactionSelect() ausgewählt werden. Bei einer Transaktion handelt es sich in HedgeTerminal in der Regel um eine gegenläufig gerichtete Position. Nachdem sie ausgewählt wurde, können ihre Eigenschaften abgerufen oder Handelsoperationen an ihr ausgeführt, zum Beispiel eine Verlusteindämmungsgrenze festgelegt oder sie geschlossen werden. Dieser Arbeitsalgorithmus ist nahezu identisch mit dem zur Bearbeitung von Aufträgen in MetaTrader 4.

Außer zu den grundlegenden Informationen bezüglich der Anzahl gegenläufig gerichteter Positionen und ihrer Eigenschaften bietet HedgeTerminal Zugriff auf Werte, die unmittelbar in MetaTrader 5 nicht zugänglich sind und komplizierte analytische Berechnungen erfordern. Zum Beispiel lässt sich die Größe des Schwunds jeder gegenläufig gerichteten Position in Erfahrung bringen, indem man lediglich eine ihrer Eigenschaften abruft. Die Anzahl der in die ausgewählte Position eingehenden Abschlüsse lässt sich ebenfalls prüfen. Dabei werden alle Berechnungen und die entsprechende Zuordnung der Abschlüsse beim Start von HedgeTerminal „hinter den Kulissen“ ausgeführt. Das ist praktisch, da das den Handel ausführende Expert-System nichts berechnen muss. Alle benötigten Informationen wurden bereits durch die einfache und verständliche Bibliothek berechnet und bereitgestellt.

Der Umstand, dass die Bibliothek und das Bedienfeld von HedgeTerminal vergleichbare Algorithmen nutzen, ermöglicht die einheitliche Wiedergabe der Daten. Deshalb können die Expert-Systeme aus dem HedgeTerminal-Bedienfeld gesteuert werden, das Bedienfeld seinerseits zeigt jede von einem Expert-System vorgenommene Änderung unverzüglich an.


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

Beigefügte Dateien |
Prototypes.mqh (15.3 KB)
Chaos2.mq5 (23.56 KB)
Grundlagen der Börsenkursbildung am Beispiel des Terminhandelsbereichs der Moskauer Börse Grundlagen der Börsenkursbildung am Beispiel des Terminhandelsbereichs der Moskauer Börse
In diesem Beitrag wird die Theorie der Kursbildung und der Besonderheiten der Verrechnung im Terminhandelsbereich der Moskauer Börse vorgestellt. Es handelt sich um einen umfassenden Überblicksartikel, der sowohl Neueinsteigern helfen soll, erste Erfahrungen im Terminhandel zu sammeln, als auch erfahrenen Devisenhändlern, die den Handel auf einer zentralisierten Plattform in Erwägung ziehen.
Das MQL5-Kochbuch: Implementierung eines Assoziativen Arrays oder eines Lexikons für raschen Datenzugriff Das MQL5-Kochbuch: Implementierung eines Assoziativen Arrays oder eines Lexikons für raschen Datenzugriff
Dieser Beitrag beschreibt einen speziellen Algorithmus mit dem auf Elemente mittels ihrer einmaligen Schlüssel zugegriffen werden kann. Als Schlüssel kann jeder einfache Datentyp verwendet werden. Er kann z.B. als String oder eine ganzzahlige Variable dargestellt werden. So einen Datenbehälter kennt man meistens als Lexikon oder ein assoziatives Array. Er bietet einen leichteren und effizienteren Weg der Problemlösung.
Das MQL5-Kochbuch: Überwachen von mehreren Timeframes in einem Fenster Das MQL5-Kochbuch: Überwachen von mehreren Timeframes in einem Fenster
In MetaTrader 5 stehen 21 Timeframes für die Analyse zur Auswahl. Sie können spezielle Diagrammobjekte nutzen, die Sie im bestehenden Diagramm platzieren können, und Symbol, Timeframe und einige weitere Eigenschaften direkt dort festlegen. Dieser Beitrag liefert detaillierte Informationen zu solchen grafischen Diagrammobjekten: Wir erstellen einen Indikator mit Steuerelementen (Buttons), die es uns ermöglichen, mehrere Diagrammobjekte gleichzeitig in einem Unterfenster einzurichten. Ferner werden Diagrammobjekte genau in das Unterfenster passen und werden automatisch angepasst, wenn die Größe des Hauptdiagramms oder des Terminalfensters verändert wird.
Die Betrachtung der CCanvas-Klasse. Wie man transparente Objekte zeichnet Die Betrachtung der CCanvas-Klasse. Wie man transparente Objekte zeichnet
Sie wollen mehr als nur komische Grafiken von gleitenden Mittelwerten? Sie möchten etwas Schöneres in Ihrem Terminal abbilden, als nur ein schlichtes, gefülltes Rechteck? Das geht! Im Terminal kann man nämlich tatsächliche attraktive Grafiken zeichnen. Und zwar durch Implementierung der CСanvas-Klasse, die zur Erzeugung von individuell angepassten Grafiken benutzt wird. Mit dieser Klasse können Sie Transparenz umsetzen, Farben mischen und sogar den Anschein von Transparenz durch Überlappung und Ineinanderlaufen von Farben erreichen.