Der Player des Handels auf Basis der Abschlusshistorie

Mykola Demko | 14 März, 2016

Besser einmal sehen als zweimal hören

Die visuelle Analyse der Handelshistorie ist ein wichtiger Bestandteil der analytischen Arbeit eines Händlers. Wäre es nicht so, dann gäbe es keine technische Analyse, die Zahlen in Bilder verwandelt. Und das versteht sich von selbst, da 80 % der menschlichen Wahrnehmung über die Augen stattfindet. Statistiken mit verallgemeinerten Informationen können viele Nuancen nicht wiedergeben. Nur eine Visualisierung mit ihrer intuitiven Darstellung von Zahlen kann das Bild vervollständigen. Etwas einmal zu sehen ist besser, als es zweimal zu hören.

In diesem Beitrag betrachten wir nicht die Erstellung eines Expert Advisors für die Automatisierung der visuellen Darstellung der Handelshistorie. Wir gehen auf die Übergabe von Informationen zwischen Objekten, die Planung riesiger Anwendungen, die Verwaltung von Diagrammen, die Synchronisierung von Informationen verschiedener Symbole usw. ein.

Traditionell möchte ich ihnen zuerst die Vorteile der Player-Anwendung und das damit verbundene Script näherbringen und anschließend mit der Analyse des Codes fortfahren.

Durchführen von Abschlüssen im MetaTrader 5 Strategietester

Die Arbeit des Players basiert auf dem HTML-Bericht von MetaTrader 5. Somit kann die Historie des Automated Trading Championship 2010 durch Einloggen mit dem erforderlichen ATC-2010-Account und Speichern der Handelshistorie als HTML-Bericht abgerufen werden.

Da der Server des Automated Trading Championship 2008 gestoppt wurde, können wir damit nicht auf die gleiche Weise verfahren. Die Seite beinhaltet einen allgemeinen Bericht über alle Teilnehmer in einem einzelnen Zip-Archiv. Automated_Trading_Championship_2008_All_Trades.zip

Das Archiv "Automated Trading Championship 2008 All Trades.zip" muss im Unterordner \Files des Installationsverzeichnisses von MetaTrader 5 entpackt werden.

Um die Historie des Automated Trading Championship 2008 zu analysieren, müssen Sie das Script Report Parser MT4 ausführen, das die Historie parst, eine Auswahl für den angegebenen Login erstellt und diese in einer binären Datei speichert. Diese binäre Datei wird vom Expert Advisor Player Report gelesen.

Der EA Player Report muss mit dem erforderlichen Login im Strategietester ausgeführt werden. Sobald die Tests abgeschlossen sind, speichern Sie einen Bericht im HTML-Format. Der angegebene Login wirkt sich nicht auf die Testergebnisse aus, wird aber als Eingabeparameter "login" im Bericht angezeigt. Damit können die Berichte weiter unterschieden werden. Da die Berichte durch denselben Expert Advisor erstellt werden, wird empfohlen, ihnen Namen zu geben, die sich vom Standardnamen unterscheiden.

Das Script Report Parser MT4 hat ebenfalls den Eingabeparameter "login", in dem sie den Login eines Teilnehmers angeben müssen, dessen Historie Sie sehen möchten. Falls Sie den Login eines Teilnehmers nicht kennen, sondern nur dessen Nickname, starten Sie das Script mit dem Nullwert (Standardwert) des Logins. In diesem Fall erstellt das Script keine Auswahl nach Login, sondern eine CSV-Datei, in der alle Logins in alphabetischer Reihenfolge aufgezählt sind. Der Name der Datei lautet "Automated Trading Championship 2008 All Trades_plus". Sobald Sie den erforderlichen Teilnehmer in dieser Datei gefunden haben, führen Sie das Script erneut mit dem angegebenen Login aus.

Somit wird durch das Zusammenspiel des Scripts Report Parser MT4 und des EA Player Report der Standard-HTML-Bericht des MetaTrader 5 Strategietesters auf Basis der Handelshistorie im MetaTrader-4-Format erstellt.

Der Expert Advisor Player Report führt die Handelstätigkeiten nicht genau wie in der Realität aus, sondern nur annähernd. Die Gründe dafür sind unterschiedliche Gebote, die Rundung der Zeit im Bericht auf Minuten und Abweichungen während der Ausführung. In den meisten Fällen beträgt die Differenz einige Punkte, und das auch nur bei 10 % der Trades. Doch das reicht bereits aus, um den Gewinn im Strategietester von beispielsweise 170.000 auf 160.000 zu senken. Alles hängt vom Volumen der Abschlüsse, bei denen es Abweichungen gab, ab.


Arbeit des Players

Wie ich bereits erwähnt habe, kann der Player zum Betrachten der Handelshistorie des Automated Trading Championship 2008 mithilfe zusätzlicher Anwendungen verwendet werden und das Automated Trading Championship 2010 kann direkt angesehen werden.

Zusätzlich unterstützt der Player alle Berichte von MetaTrader 5, sodass Sie die Handelshistorie jedes beliebigen im Strategietester ausgeführten Expert Advisors oder die Historie des manuellen Handels, die nicht vom Tester formatiert ist, sondern in der Registerkarte "History" (Historie) im Fenster "Toolbox" als Bericht gespeichert wurde, ansehen können.

Parameter des Expert Advisors Player History Trades exp v5:

Ein Bericht des MetaTrader 5 Strategietesters wird als Eingabedatei für den Player der Abschlusshistorie genutzt. Der Name der Berichtdatei muss als Eingabeparameter "Name der HTML-Datei des Strategietester-Berichts" des EA Player History Trades exp v5 angegeben werden. Beim Starten des Players kann der Benutzer in den Eingabevariablen "Beginn der Historie" und "Ende der Historie" einen abzuspielenden Zeitraum festlegen.

Werden diese Variablen nicht festgelegt, bezieht der Player sie aus der Handelshistorie vom ersten bis zum letzten Abschluss. Die Anzahl an Symbolen für den Handel macht keinen Unterschied. Es wird nur der Zeitpunkt des ersten und letzten Abschlusses berücksichtigt.

Zusätzlich kann der Benutzer die Namen der Symbole, deren Diagramme analysiert werden sollen, festlegen. Diese Namen müssen als Aufzählung in der Variable "Liste erforderlicher Diagramme" festgelegt werden. Die Analyse dieser Variable ignoriert die Groß- und Kleinschreibung und die Art von Trennzeichen. Wird diese Variable nicht festgelegt, werden alle für das Konto gehandelten Symbole geöffnet. Und teilweise gibt es sehr viele von ihnen.

Zum Beispiel nutzte Manov für seinen Handel 12 Währungspaare. Ich empfehle, nicht mehr als vier Symbole auf einmal festzulegen. Erstens lassen sie sich bequem anordnen. Zweitens verlangsamt eine große Menge an Diagrammen das Abspielen. Da jedes Symbol in der allgemeinen Schleife verarbeitet wird, führt eine Erhöhung der Menge von Symbolen zu einer Verlangsamung der Erzeugung von Ticks.

Der Player funktioniert auch dann, wenn Sie ein Symbol festlegen, das im Handel nicht verwendet wurde. In diesem Fall zeigt das Diagramm keine Abschlüsse an, sondern verhält sich wie die anderen Diagramme. Zusätzlich wird der Bilanz-Indikator an das Diagramm angehängt. Dieser zeigt allerdings nur die Historie der allgemeinen Bilanz in all ihren Varianten an.

Die Beschreibung des Parameters "Löschen des Diagramms beim Löschen des EA" habe ich allerdings absichtlich übersprungen. Dieser betrifft das Verhalten des Expert Advisors, aber nicht dessen Verwaltung. Der springende Punkt ist, dass der Expert Advisor für seine Arbeit zahlreiche Informationen analysiert. Ich habe entschieden, dass einige der Informationen, über die der EA verfügt, für die Analyse in Form von Dateien nützlich sein wird. Der Expert Advisor erstellt CSV-Dateien, die Handelstätigkeiten für jedes Symbol enthalten, und eine Datei mit den synchronisierten Bilanzen aller Symbole, die für das Suchen eines Symbols in einem Korb mit mehreren Währungen nützlich sein kann.

Dieselbe Variable wird auch genutzt, um Diagramme zu löschen, die automatisch vom Expert Advisor geöffnet wurden. Traditionell sollte der EA seinen Arbeitsplatz am Ende seiner Arbeit aufräumen. Doch wenn der Benutzer ein Diagramm ohne Steuerung durch den EA analysieren möchte, sollte er den EA mit dem Parameter "Löschen von Diagrammen beim Löschen des EA" auf 'false' starten.

Die nachfolgenden Parameter sind nicht sehr wichtig.

Der Zeitraum des Generators legt den Ausgangsparameter des Tick-Generators fest. Der Begriff "Tick" wird hier nicht in seiner herkömmlichen Bedeutung verwendet, sondern steht für die Änderung des Niveaus. Im Expert Advisor werden Ticks gemäß vier Punkten von Balken generiert. Der Parameter "Zeitraum des Generators" legt den Ausgangszustand des Generators fest. Anschließend können Sie diesen Parameter während der Arbeit des Players anpassen.

Warum erzeugen wir nicht alle Zeiträume ab M1? Warum müssen wir den Zeitraum des Generators ändern? Das Problem ist, dass die Balken größerer Timeframes zahlreiche M1-Balken enthalten. Deshalb müssen wir möglicherweise den Prozess der Generierung beschleunigen. Deshalb wurde die Möglichkeit, die Zeiträume zu ändern, implementiert. Nicht alle Timeframes werden im Generator umgesetzt, sondern nur bestimmte. Ihre Änderung im Code wird später beschrieben.

Der Parameter "Schriftgröße der Kommentare zu Abschlüssen" kann hilfreich sein, beispielsweise, wenn Kommentare zu Abschlüssen die Ansicht der Abschlüsse selbst behindern. Wenn Sie die Größe als 1 festlegen, wird die Schrift als dünne Linie dargestellt und behindert nicht die Betrachtung. Dabei können Sie das Volumen des Abschlusses und die Position in der Registerkarte "List of objects" (Objektliste) sehen, nachdem Sie den Objektnamen dem Tooltip entnommen haben.

Die Handelshistorie wird mit separaten Abschlüssen gezeichnet, allerdings hängt die gezeichnete Linie vom Typen der Position ab.

Mithilfe der "Farbe von buy-Operationen" und "Farbe von sell-Operationen" können Sie Ihre gewünschten Farben festlegen.

Anzeige der Handelshistorie

Im Screenshot sehen Sie, dass sich das Niveau der Position oft vom Niveau des Abschlusses unterscheidet.

Der Gewinn wird allerdings auf Basis des Positionsniveaus berechnet. Deshalb habe ich beschlossen, die Position mit einer Trendlinie anzuzeigen und die Niveaus von Positionen und Abschlüssen durch eine vertikale Linie zu verbinden. Der Kommentar neben dem Positionsniveau zeigt die folgenden Informationen:

[deal volume|position volume]

Wenn der Typ des Abschlusses nicht dem Typ der Position entspricht (beispielsweise bei einer Teilschließung), werden Volumina mit zusätzlichen Zeichen angezeigt:

[<deal volume>|position volume]

An erster Stelle sehen Sie das Volumen des Abschlusses, wie es im Handelsbericht angezeigt wird. Das Volumen der Position wird auf Basis des vorherigen Zustands der Position und der durch den Abschluss herbeigeführten Änderungen berechnet.

Der Parameter "Menge der Geschwindigkeiten" regelt die Anzahl der Stufen bei der Verringerung der Abspielgeschwindigkeit. Der Player wird bei maximaler Geschwindigkeit gestartet. Im weiteren Verlauf können Sie die Geschwindigkeit innerhalb des Werts des Parameters "Menge der Geschwindigkeiten" verringern und erhöhen. Somit stellen der Geschwindigkeitsbutton und der Zeitraum des Generators eine vollständige Auswahl an Werkzeugen für die Verwaltung der Abspielgeschwindigkeit der Handelshistorie dar.

Der letzte Parameter ist die "vertikale Abmessung des Fortschritt-Buttons". Diesen habe ich für Benutzer erstellt, die große Buttons im Fortschrittsbalken bevorzugen. Allgemein war es mein Ziel, ein Verstecken des Diagramms hinter den Bedienelementen zu vermeiden. Deshalb ist der Parameter "vertikale Abmessung des Fortschritt-Buttons" auf 8 festgelegt.

Fahren wir nun mit den Bedienelementen des Players fort.

Bedienelemente des Players

Die Geschwindigkeit wird mit den Pfeilen nach links und rechts gesteuert. Der Kontrollmodus hängt vom Zustand des mittleren (quadratischen) Buttons ab. Im ungedrückten Zustand wird die Geschwindigkeit geändert, im gedrückten der Zeitraum des Generators.

Das Steuerobjekt des Bilanz-Indikators wird als volles Oval angezeigt, besteht allerdings tatsächlich aus zwei großen Buttons, die weit über die Grenzen ihrer visuell dargestellten Größe hinausgehen. Mit dem linken Button wird der Bilanz-Indikator dem Diagramm hinzugefügt und daraus entfernt, mit dem rechten werden Dateninhalte gesteuert.

Im Zustand "All" werden Informationen über die Gesamtbilanz des Kontos angezeigt. Im Zustand "Sum" wird eine Auswahl der Bilanz für das Diagrammsymbol, auf dem der Indikator ausgeführt wird, angezeigt. Die Steuerung des Indikators ist asynchron. Das bedeutet, dass der Indikator auf einem Diagramm ausgeführt werden kann, auf einem anderen nicht. 

Bilanz-Indikator

Das Steuerobjekt des Bilanz-Indikators stellt die einzige Ausnahme bei der Synchronisation von Diagrammen dar. Alle anderen Steuerobjekte sind synchronisiert. In anderen Worten: Änderungen, die an einem Symbol vorgenommen werden, werden automatisch auf die anderen Symbole angewendet.

Der Button Abspielen/Stopp zeigt an, welche Operation durchgeführt wird, sobald Sie ihn drücken. Während des Abspielens werden zwei kleine Linien angezeigt, die bedeuten, dass die Operation pausiert wird, wenn Sie darauf drücken. Umgekehrt wird im pausierten Zustand ein Dreieck angezeigt. Wenn Sie darauf drücken, beginnt der Player seine Operation.

Die Fortschrittslinie besteht aus 100 Steuerbuttons, die als Trigger funktionieren: Wenn ein Button gedrückt wird, werden alle anderen Buttons losgelassen. Da es 100 Buttons gibt, ist der Abspielzeitraum in 100 Bereiche aufgeteilt. Wenn die Menge der Balken nicht durch 100 teilbar ist, wird der Rest dem letzten Teil hinzuaddiert. Deshalb beinhalten die Einstellungen die Parameter "Beginn der Historie" und "Ende der Historie". Durch eine Änderung dieser Parameter können Sie zum erforderlichen Zeitraum der Historie navigieren.

Durch Drücken eines Buttons ändert der Benutzer das Datum des internen Tick-Generators und navigiert durch den Null-Balken. Wird kein Button gedrückt, nachdem die innere Zeit des Generators bereits nach außerhalb des aktiven Buttons verschoben wurde, führt der Player die entsprechende Umschaltung selbstständig aus.

Somit dient die "Fortschrittslinie" gleichzeitig als Anzeige des Fortschritts und als aktives Steuerelement der Navigation. Die Steuerobjekte des Players werden automatisch ausgeblendet und in der Mitte des Diagramms eingeblendet. Wenn Sie also einen bestimmten Button auf der Fortschrittslinie drücken müssen, vergrößern Sie das Diagramm auf den ganzen Bildschirm.

Sehen wir uns nun das Verhalten der Diagramme an, die durch den Player verwaltet werden. Der Player führt die Synchronisierung aller Diagramme aus, doch das bedeutet nicht, dass Änderungen der Skala, des Diagrammtypen, des Farbschemas, Verschiebungen des Null-Balkens usw., die im Hauptdiagramm durchgeführt werden, in den anderen Diagrammen wiederholt werden.

Zu den Änderungen gehören auch Änderungen des Timeframes. Hier sollten wir festhalten, dass das Diagramm, das vom Player als Hauptdiagramm behandelt wird, dasjenige ist, in dem die Bedienelemente angezeigt werden, nicht das mit der blauen Aktivitätslinie. Für gewöhnlich ist dies dasselbe Diagramm, doch das ist nicht immer der Fall. Klicken Sie im Diagrammfeld auf ein Diagramm, um es zu aktivieren.

Bei der Verwendung des Players gibt es eine Besonderheit. Wenn sich zwei Objekte im gleichen Feld befinden, funktionieren die Buttons nicht mehr. Deshalb müssen Sie manchmal zu einem anderen Diagramm wechseln oder die vertikale Skala des Diagramms ändern, um einen Button zu drücken, wenn die bid-Linie das Player-Feld überkreuzt.


Das Video demonstriert die Wiedergabe des Handels von Manov, einem der Teilnehmer des ATC 2010. Dazu verband ich sein Konto im Client Terminal mit den Parametern login=630165 und password=MetaTrader. Der Handelsbericht wurde unter dem Namen ReportHistory-630165.html im Ordner Datenordner_des_Terminals\MQL5\Files gespeichert. Sie können diese Datei als Archiv herunterladen und im angegebenen Ordner entpacken.

Vorbereitung zum Start

  1. Damit alles funktioniert, laden Sie player_history_trades.zip herunter und entpacken Sie das Archiv in den Ordner Datenordner_des_Terminals/MQL5/Indicators.
  2. Öffnen Sie den kopierten Ordner Player History Trades und kompilieren Sie die vier Dateien im Stammverzeichnis in MetaEditor. Die Reihenfolge der Kompilierung der Dateien ist nicht ausschlaggebend.
  3. Stellen Sie sicher, dass der erforderliche Zeitraum der Historie für alle Symbole im Handelsbericht im Timeframe M1 verfügbar ist. Öffnen Sie dazu manuell das erforderliche Diagramm mit dem Timeframe M1, platzieren Sie eine vertikale Linie und öffnen Sie die Objektliste mithilfe der Tastenkombination Strg+B oder aus dem Kontextmenü. Passen Sie dann das Datum der vertikalen Linie an das Datum des Handelsbeginns an.
  4. Klicken Sie dann auf "Show" (Anzeigen). Falls es keine Gebote gibt, kann dies zwei Gründe haben. Entweder werden sie nicht heruntergeladen oder der Parameter "Max. Balken im Diagramm" ist zu klein. Sie sehen den Parameter unter Tools->Options->Charts (Tools->Optionen->Diagramme).

Nun sollte alles funktionieren.


Start der Entwicklung

Um eine Anwendung zu entwickeln, sollten Sie einen Plan haben, der sich im weiteren Verlauf in ein Blockdiagramm und dann in einen Code verwandelt. Doch das Projekt selbst beginnt schon früher. Der Beginn jedes Projekts sind die Anwendungseigenschaften, die der Benutzer benötigt. Welche Eigenschaften sollte der Player der Handelshistorie also haben? 

  1. Mehrwährungsfähigkeit.
  2. Automatische Öffnung erforderlicher Diagramme.
  3. Praktische Navigationsoberfläche und Möglichkeit, die Historie in beiden Richtungen durchzublättern.
  4. Synchrone Anzeige in allen Diagrammen.
  5. Beginn/Unterbrechung der Wiedergabe.
  6. Auswahl (und Standardmodus) der Anzahl und Symbole anzuzeigender Diagramme.
  7. Auswahl (und Standardmodus) des Zeitraums, mit dem der Player arbeiten wird.
  8. Anzeige der Abschlusshistorie im Diagramm.
  9. Anzeige der Bilanz- und Eigenkapitalhistorie.
  10. Separate Anzeige der Bilanz (des Eigenkapitals) eines Symbols und der Gesamtbilanz (des Gesamteigenkapitals) eines Kontos.

Die ersten vier Punkte bestimmen das allgemeine Konzept. Die restlichen Eigenschaften bestimmen die Richtung der Implementierung von Methoden.

Der allgemeine Plan der Arbeit des Players:

  1. Laden eines HTML-Berichts;
  2. Parsen des Berichts auf Abschlüsse und Wiederherstellen der Positionshistorie;
  3. Vorbereiten der Abschlüsse als Warteschlange von Ordern zum Öffnen/Schließen;
  4. Anzeige der Dynamiken der Abschlusshistorie mit Berechnung erforderlicher Anteile in Form von Indikatoren auf Anweisung des Benutzers (Diagramme von Eigenkapital, Drawdowns usw.);
  5. Organisieren der Anzeige einer Informationsleiste im Diagramm mit den restlichen Anteilen.

Zusätzlich wird ein spezieller Expert Advisor benötigt, der im Strategietester gemäß einem MetaTrader-4-Bericht handelt:

  1. Geparste Abschlüsse müssen als binäre Datendatei für den Expert Advisor geschrieben werden;
  2. Erstellen des Berichts des MetaTrader 5 Strategietesters.

Dies ist das allgemeine Schema für den Beginn der Entwicklung, eine Festlegung von Anforderungen. Sobald Sie darüber verfügen, können Sie das Schreiben des Codes von oben nach unten planen, vom Konzept bis zur Umsetzung der Funktionalität.

Um den Beitrag nicht zu lang zu machen, beschreibe ich nur die wesentlichsten Teile des Codes. Sie sollten beim Lesen des Codes auf keine Probleme stoßen, da er gut kommentiert ist.

Order und Abschlüsse

Es gibt derzeit zwei Handelskonzepte. Das alte Konzept, das in MetaTrader 4 genutzt wird, und das Konzept, das für reale Gebote und in MetaTrader 5 genutzt wird, das sogenannte "Netting"-Konzept. Eine detaillierte Beschreibung der Unterschiede zwischen den beiden Konzepten finden Sie im Beitrag Order, Positionen und Abschlüsse in MetaTrader 5.

Ich werde nur einen wesentlichen Unterschied beschreiben. In MetaTrader 4 kann eine Order als Container dargestellt werden, der Informationen über Öffnungszeiten, Öffnungspreise und Handelsvolumina speichert. Und während das Tor des Containers geöffnet ist, befindet er sich im aktiven Handelszustand. Sobald Sie den Container schließen, werden alle Informationen daraus in die Historie verschoben.

In MetaTrader 5 werden Positionen als solche Container genutzt. Doch der wesentliche Unterschied ist das Fehlen einer Historie von Positionen. Es gibt nur die gemeinsame Historie von Ordern und Abschlüssen. Und obwohl die Historie alle nötigen Informationen enthält, um die Historie der Positionen wiederherzustellen, brauchen Sie etwas Zeit, um Ihre Denkweise anzupassen.

Mithilfe der Identifikatoren ORDER_POSITION_ID bzw. DEAL_POSITION_ID können Sie herausfinden, zu welcher Position eine ausgewählte Order oder ein Abschluss jeweils gehört. Um die Historie in ein für MetaTrader 5 geeignetes Format zu übertragen, teile ich die Order der MetaTrader-4-Historie in zwei unterschiedliche Trades auf: öffnende und schließende Trades.


HTML-Parser

Für diejenigen, die mit dem Computer-Slang nicht vertraut sind, lege ich dar, war das Wort "parsen" bedeutet. Parsing ist die syntaktische (grammatikalische oder lexikalische) Analyse eines Textes oder einer beliebigen Abfolge von Lexemen (Symbole, Wörter, Bytes usw.), die prüft, ob der eingegebene Text einer vorgegebenen Grammatik entspricht, und einen Syntaxbaum erstellt, anhand dessen Sie weitere Berechnungen oder Umwandlungen durchführen können.

Die beiden großen Klassen CTable und CHTML werden im Parser verwendet. Die Verwendung der CTable-Klasse wird im Beitrag Elektronische Tabellen in MQL5 detailliert beschrieben, deshalb beschreibe ich sie an dieser Stelle nicht noch einmal.

Zum Parsen von HTML habe ich die CHTML-Klasse entwickelt. Ich hatte zuerst die Idee, einen eigenen Beitrag aus ihrer Beschreibung zu machen. Doch die Klasse ist zu simpel für einen eigenen Beitrag, deshalb beschreibe ich sie hier kurz.

Das allgemeine Konzept der Klasse kann durch den Begriff "Tag" zusammengefasst werden. Ein Tag kann als Funktion mit Beilagen dargestellt werden. Beispielsweise Tag(header,casket): Hier ist 'header' der Tag-Titel (Tag-Variablen, die das Erscheinungsbild einer Seite definieren, sind normalerweise hier festgelegt) und 'casket' der Inhalt des Tag-Containers. Solche Tags bilden die gesamte HTML-Sprache.

Die allgemeine Struktur der Klasse kann als dreistufiger Aufruf von Objekten dargestellt werden. Eine Instanz der CHTML-Klasse erstellt die Objekte aller möglichen Tags innerhalb ihres Körpers. Die Funktionen von Tags werden durch ein Template erstellt und unterscheiden sich nur durch ihre Namen und das Setzen von zwei Flags voneinander.

Ein Flag bestimmt das Vorhandensein von header, das andere das Vorhandensein von casket. Diese Flags ermöglichen die Anwendung einer gemeinsamen Struktur auf alle Tags. Jede Tag-Instanz erstellt eine Instanz der Klasse CTegs in ihrem Körper. Diese Klasse beinhaltet gemeinsame Methoden für alle Tags und führt die Hauptoperationen bei der Suche von erforderlichen Tags im Text eines Dokuments durch.

So sieht also der dreistufige Aufruf aus:

h.td.base.casket

Dieser Eintrag bedeutet, dass das Objekt 'h' den Wert der Variable 'casket' durch das Objekt 'td' (das eine Instanz des Tags <td header >casket</td> ist) über das geschachtelte Objekt 'base' (das ein Objekt der Klasse CTegs ist) aufruft.

Ebenso enthält die Klasse Methoden zum Suchen der Tags. Diese sind in der öffentlichen Methode

h.td.Search(text,start);

kombiniert, die den Suchpunkt des Tag-Endes ausgibt und die Variablen 'header' und 'casket' des Tags befüllt.

Die anderen in dieser Klasse vorbereiteten Methoden werden nicht verwendet, also werde ich sie nicht beschreiben, da noch viele weitere interessante Sachen besprochen werden müssen.

Zum Ende der Beschreibung der Arbeit mit HTML-Dokumenten möchte ich erwähnen, dass in diesem Beitrag zwei Typen von Parsern verwendet werden. Sie unterscheiden sich nur in ihrer Art des Speicherns von Informationen aus der Datei. Der erste Typ speichert das gesamte Dokument in einer einzelnen Variable des Typen 'string', die im Player verwendet wird. Der zweite Typ parst den Bericht Zeile für Zeile. Er wird im Script für die Erstellung der Historie der Meisterschaft 2008 verwendet.

Warum nutze ich zwei Ansätze? Für die korrekte Arbeit der Funktionen der CTegs-Klasse muss das gesamte Tag im zu analysierenden String platziert werden. Und das ist nicht immer möglich, beispielsweise bei Tags wie table, html, body (sie sind mehrzeilig). Eine Variable des Typen string ermöglicht (nach meinen Berechnungen) die Speicherung von 32750 Symbolen ohne Tabulierungssymbole. Und mit '\r' (nach jedem 32748. Symbol) konnte ich bis zu 2.000.000 Symbole speichern. Nachdem ich diesen Wert erreicht hatte, brach ich meine Versuche ab. Höchstwahrscheinlich können noch mehr Symbole gespeichert werden.

Warum nutze ich also zwei Ansätze? Der springende Punkt ist, dass Sie für einen universell nutzbaren Parser des Players eine geeignete Tabelle finden müssen. Die erforderlichen Tabellen für den Testerbericht und den Bericht der Abschlusshistorie befinden sich an unterschiedlichen Orten. Um die Flexibilität zu erhalten (damit der Parser beide Berichte versteht), nutzte ich für die Tabelle das Suchschema mit 'td'-Tag, das "Abschlüsse" enthält.

Die Struktur des Berichts der Meisterschaft 2008 ist bekannt und die erforderliche Tabelle muss nicht gesucht werden. Allerdings ist der Bericht riesig (35 MB) und das Platzieren des gesamten Berichts in einer einzelnen Variable würde viel Zeit in Anspruch nehmen. Diese Situation erfordert den zweiten Parsing-Ansatz.


Der Player

Im Abschnitt "Start der Entwicklung" werden 10 Anforderungen an den Player beschrieben. Da die Mehrwährungsfähigkeit an erster Stelle steht, muss der Expert Advisor Diagramme verwalten. Es wäre logisch, wenn jedes Diagramm durch ein separates Objekt verarbeitet wird, das über alle erforderlichen Funktionen verfügt, damit der Player arbeiten kann.

Da wir mit historischen Daten arbeiten, brauchen wir ein separates Exemplar der Historie für den ununterbrochenen Betrieb, anstatt zu hoffen, dass wir diese jederzeit abrufen können. Zusätzlich wäre ein wiederholtes Abrufen der Historie Verschwendung im Vergleich zu ihrer Aufbewahrung im Player. Insgesamt ergibt sich das folgende Schema:

Allgemeines Schema des Players der Handelshistorie

Die objektorientierte Programmierung (OOP) ermöglicht Ihnen das Schreiben ziemlich großer Anwendungen mithilfe von Blocksystemen. Der zu entwickelnde Teil des Codes des Expert Advisors kann vorher in ein Script geschrieben, debuggt und dann mit minimaler Anpassung mit dem Expert Advisor verbunden werden.

Dieses Entwicklungsschema ist praktisch, da Sie sicher sein können, dass der verbundene Code fehlerfrei ist (da er im Script fehlerfrei ausgeführt wird) und etwaige gefundene Bugs Fehler in der Anpassung sind. Diesen Vorteil gibt es nicht, wenn der Code von unten nach oben geschrieben wird und Sie alles als Verfahren an einem Ort beschreiben. Zudem können an jedem Ort innerhalb der Anwendung neue Bugs auftauchen.

Deshalb ist die Programmierung von oben nach unten durch die Einfachheit und Schnelligkeit beim Schreiben einer Anwendung überlegen. Wenn Sie Fragen "Was genau ist hier einfach?", antworte ich mit einer Allegorie: Es ist schwierig, Fahrradfahren zu lernen, doch wenn Sie es einmal gelernt haben, bemerken Sie den Prozess nicht einmal. Sie genießen einfach schnelles Fahren. Sobald Sie sich die OOP-Syntax angeeignet haben, genießen Sie große Vorteile.

Für die weitere Beschreibung muss ich drei Begriffe der OOP erläutern: Assoziation, Aggregation und Komposition.

Da die Assoziation die Aggregation und die Komposition einschließt, werden bei einer Detailanalyse alle Fälle, die nicht als Aggregation oder Komposition eingestuft werden können, als Assoziation eingestuft. Alle drei Idiome werden allgemein als Assoziation bezeichnet.

class Base
  {
public:
                     Base(void){};
                    ~Base(void){};
   int               a;
  };
//+------------------------------------------------------------------+

class A_Association
  {
public:
                     A_Association(void){};
                    ~A_Association(void){};
   void              Association(Base *a_){};
   // At association, data of the bound object 
   // will be available through the object pointer only in the method, 
   // where the pointer is passed.
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class A_Aggregation
  {
   Base             *a;
public:
                     A_Aggregation(void){};
                    ~A_Aggregation(void){};
   void              Aggregation(Base *a_){a=a_;};
   // At aggregation, data of the bound object 
   // will be available through the object pointer in any method of the class.
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class A_Composition
  {
   Base             *a;
public:
                     A_Composition(void){ a=new Base;};
                    ~A_Composition(void){delete a;};
   // At composition, the object becomes the class member.
  };

In MQL5 gibt es eine Funktion zum Übergeben eines Pointers über einen Parameter: 

GetPointer(pointer)

Ihr Parameter ist der Objekt-Pointer.

Zum Beispiel:

void OnStart()
  {
   Base a; 
   A_Association b;
   b.Association(GetPointer(a));
  }

Funktionen, die in OnInit() aufgerufen werden, nutzen in meinem Code oft die Assoziation. Die Komposition wird in der CHTML-Klasse angewendet. Die Aggregation und Komposition nutze ich zusammen, um Objekte innerhalb der CPlayer-Klasse zu verbinden. Beispielsweise erstellen die Objekte der Klassen CChartData und SBase mithilfe der Aggregation ein gemeinsames Datenfeld für alle Objekte, die mithilfe der Komposition im Player erstellt werden.

Optisch kann dies folgendermaßen dargestellt werden:

Verbinden von Daten

Klassen, deren Objekte gemeinsam in der CPlayer-Klasse erstellt werden, haben eine Template-Struktur mit einer weiteren Erweiterung der Funktionalität. Die Nutzung der Templates wird im Beitrag Nutzung von Pseudo-Templates als Alternative für C++-Templates beschrieben, deshalb werde ich sie an dieser Stelle nicht detailliert beschreiben.

Ein Template für die Klasse sieht so aus:

//this_is_the_start_point
//+******************************************************************+
class _XXX_
  {
private:
   long              chart_id;
   string            name;
   SBase            *s;
   CChartData       *d;
public:
   bool              state;
                     _XXX_(){state=0;};
                    ~_XXX_(){};
   void              Create(long Chart_id, SBase *base, CChartData *data);
   void              Update();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void _XXX_::Create(long Chart_id, SBase *base, CChartData *data)
  {
   chart_id=Chart_id;
   s=base; // binding data to the player structure
   d=data; // binding data to the chart structure
   name=" "+ChartSymbol(chart_id);

   if(ObjectFind(chart_id,name)<0)// if there is no object yet
     {//--- try to create the object         
      if(ObjectCreate(chart_id,name,OBJ_TREND,0,0,0,0,0))
        {//---
        }
      else
        {//--- failed to create the object, tell about it
         Print("Failed to create the object"+name+". Error code ",GetLastError());
         ResetLastError();
        }
     }       
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void _XXX_::Update()
  {
  };
//+******************************************************************+
//this_is_the_end_point

Ich habe also die leeren Klassen nach dem Template erstellt, sie verbunden, geprüft, ob sie alle Anfragen korrekt verarbeiten, und erst danach habe ich angefangen, die Kompositionsklassen mit den nötigen Funktionen zu füllen. Das ist Programmierung von oben nach unten. Bei jedem möglichen Fehler wissen Sie, wo Sie nach dem Grund suchen müssen.

Nun, da das allgemeine Konzept des Aufbaus geklärt ist, können wir mit den Details fortfahren.

Sehen wir uns als Erstes die Arbeit von Funktionen an, die im Expert Advisor Player History Trades exp v5 deklariert sind.

Wie gewöhnlich, bereitet die Funktion OnInit() Informationen vor. Sie erstellt ein Objekt der Klasse CParser_Tester, das einen Bericht des Strategietesters parst, die Liste aller gehandelten Finanzinstrumente abruft, Abschlüsse verarbeitet, Volumina und Niveaus von Positionen berechnet und letztendlich die Historie im Diagramm zeichnet. Dieser letzte Punkt ist der Grund, weshalb das Objekt nicht sofort nach der Übergabe der Daten gelöscht wird. Dies liegt daran, dass die Informationen vorbereitet werden, bevor die Diagramme geöffnet werden. Und die grafischen Objekte benötigen eine Diagramm-ID zum Zeichnen. Deshalb wird das Objekt der Klasse CParser_Tester später gelöscht.

Weiterhin wird die Funktion Balance_Process() aufgerufen, da wir über die Namen der für den Handel verwendeten Symbole verfügen. Sie berechnet die Bilanzen und Eigenkapitale aller an sie übergebenen Symbole sowie die Gesamtbilanz und das Gesamtkapital auf Basis der M1-Historie.

In diesem Teil ist die Anwendung besonders empfindlich gegen mangelnde Informationen. Deshalb habe ich eine Unterbrechung der Ausführung des EA eingebaut, falls die Informationen eines der Symbole nicht heruntergeladen werden. Wenn die Anwendung unterbrochen wird, zeigt sie einen Alarm mit den Symbolinformationen an, die heruntergeladen werden müssen.

Das Ergebnis der Arbeit der Funktion Balance_Process() sind binäre Dateien der Historie von Bilanzen und Eigenkapital auf M1, die durch den Bilanz-Indikator weiter in die benötigten Zeiträume aufgeteilt werden. Hier greife ich aber ein bisschen vor, da die Arbeit des Bilanz-Indikators später beschrieben wird.

Der nächste Schritt zum Starten des Expert Advisors ist die Auswahl von Symbolen. Hier analysieren wir den Eingabeparameter "Liste erforderlicher Diagramme". Falls das erforderliche Symbol sich auf der Liste "Marktbeobachtung" befindet, fügen Sie es dem Array von Symbolen hinzu. Auf diese Weise schützen wir uns vor "Narren", da Benutzer "abrakadabra" als Symbolname eingeben oder sich einfach vertippen können.

Da wir über die geprüfte Liste von Symbolen verfügen, die der Benutzer für die Eröffnung angefragt hat, können wir die Diagramme öffnen. Das geschieht mithilfe folgender Funktion:

ChartOpen(symbol,period)

Diese Funktion öffnet ein Diagramm mit dem Symbol und dem Zeitraum, die in Parametern an sie übergeben wurden. In der Theorie gibt diese Funktion die ID des geöffneten Diagramms aus, doch das geschieht nicht immer.

Dies führt dazu, dass die ID verlorengeht und die Anwendung nicht richtig funktioniert. Um das zu vermeiden, habe ich zwei Funktionen erstellt:

ChartTotal(arrayID);   // get the list of charts before opening additional charts
CurrentChart(arrayID); // get the list of chart for operation

Eine Funktion wird vor dem Öffnen der Diagramme ausgeführt, die andere danach. Die Funktion ChartTotal() erhält die Liste von Diagrammen, die vor dem Start des EA geöffnet wurden (einschließlich des Diagramms, auf dem der EA ausgeführt wird) und speichert ihre IDs im Eingabe-Array.

Die Funktion CurrentChart() erhält diese Informationen, erstellt eine neue Liste unter Berücksichtigung der bereits geöffneten Diagramme und übergibt dann gemäß den Unterschieden zwischen den Listen die IDs von Diagrammen, die durch den EA erstellt wurden, an das Parameter-Array. Dieses Schema ist sehr zuverlässig, da es auf der Tatsache der Eröffnung eines Diagramms basiert.

Nun, da wir über die IDs der erforderlichen Diagramme verfügen, können wir sie unter unsere Kontrolle bringen. Gehen Sie hierzu alle Diagramme in einer Schleife durch und zeichnen Sie mithilfe des Objekts der Klasse CParser_Tester (wie Sie sich erinnern, habe ich vorher gesagt, dass wir es noch brauchen würden) die Historie der Abschlüsse und erstellen Sie die Objekte für die Verwaltung des Diagramms.

Als letzten Schritt in OnInit() erstellen Sie den Timer und rufen ihn für die Arbeit auf. Alle weiteren Schritte werden in OnTimer() durchgeführt.

Das erste Problem beim Erstellen des Players erscheint bereits im Anfangsstadium der Entwicklung. Es ist das Problem der Erstellung eines Timers. Mit der Funktion EventSetTimer(timer) lässt sich ein Timer mit mindestens 1 weiterer Währung erstellen. Mit dieser Variante würden Ticks einmal pro Sekunde erzeugt. Selbst unter Berücksichtigung der Einschränkungen der menschlichen Wahrnehmung ist eine Sekunde zu lang. Ich brauche mindestens 100 Millisekunden.

Deshalb habe ich eine Schleife innerhalb des Timers eingerichtet, die einige Millisekunden vor einem neuen Timer-Ereignis austritt. Doch diese Variante machte viele technische Lösungen unmöglich. Beispielsweise verschwindet die Möglichkeit, Ereignisse zu erhalten, da sie konstant darauf warten, dass der Timer den Zyklus verlässt. Durch das Fehlen der Möglichkeit, Ereignisse zu erhalten, verschwindet auch die Möglichkeit, Objekte von Playern im Indikator zu platzieren und parallele Berechnungen aller Diagramme zeitgleich durchzuführen. Doch der Expert Advisor arbeitet sogar bei einer stufenweisen Verarbeitung von Diagrammen noch ziemlich schnell.

Das Ereignis der Diagrammaktivierung wird durch die zusammengesetzte Klasse CClick ersetzt, deren Objekte ein Signal zum Ändern des aktiven zu verarbeitenden Diagramms im Zyklus der Funktion Click(n) erzeugen. Die Funktion Click() ist ein Trigger, der Änderungen am Button für die Diagrammaktivierung nachverfolgt. Wenn sie feststellt, dass der Button gedrückt wird, schaltet sie alle anderen Objekte in den passiven Zustand. Der Button für die Diagrammaktivierung ist immer nahe beim Benutzer, aber nicht sichtbar, da er die Größe des gesamten Diagramms und die Farbe des Hintergrunds hat und sich im Hintergrund befindet. Wenn ein Diagramm aktiviert wird, wird der Button hinter die sichtbaren Grenzen des Diagramms verschoben, wodurch es uns ermöglicht wird, die grafischen Objekte der Player-Bedienelemente zu sehen, die im passiven Modus durch den Button für die Diagrammaktivierung verdeckt werden.

Da wir das Hauptdiagramm mithilfe der Funktion Click() gefunden haben, gehen Sie zur Berechnung der Zeitbewegung und rufen Sie die Funktion Progress(Time) des aktiven Players auf. Diese Funktion führt die folgenden Berechnungen durch: Überprüfung, ob ein Benutzer Navigationshandlungen durchführt; falls nicht: Überprüfung, ob es Zeit ist, zum nächsten Balken zu wechseln; falls dies der Fall ist: Überprüfung, ob der Fortschritt in den nächsten Bereich verschoben werden sollte.

Wenn wir die Funktion Progress(Time) am Ende verlassen, verfügt der Zyklus über die Informationen über die aktuelle Zeit, die für weitere Berechnungen genutzt werden. Anschließend werden die Einstellungen des aktiven Diagramms in die untergeordneten Diagramme kopiert. Dies geschieht über eine Schleife in der Funktion CopyPlayer(n). Gehen Sie anschließend in der Funktion Play(Time) zur Ausführung aller Änderungen, die am Diagramm vorgenommen werden sollen, damit der Benutzer glaubt, dass die Zeit sich bewegt, Gebote eingehen und Handelstätigkeiten ausgeführt werden.

Zusammengesetzte Klassen des Players.

  1. CArrayRuler* –  speichert und durchsucht Informationen für die schnelle Bewegung zwischen den Balken eines laufenden Timeframes.
  2. CRuler*         –  speichert und durchsucht Informationen der M1-Historie für die Erzeugung von Ticks.
  3. CSpeed          –  kontrolliert die Geschwindigkeitseinstellungen und den Zeitraum des Tick-Generators.
  4. CProgress      –  kombiniert alle Fortschrittsbuttons in einem einzelnen Objekt, überwacht, ob nur ein Button gedrückt wird, ändert die Farben der Buttons.
  5. CPlay             –  startet und stoppt den Player und kontrolliert den Bilanz-Indikator.
  6. CClick            –  verantwortlich für die Signale der Diagrammaktivierung.
  7. CBackGround  –  das Objekt verbirgt den Null-Balken vor Benutzern sowie zukünftige Balken, wenn der Status Diagrammübergang vom rechten Rand aktiviert ist.
  8. CBarDraw      –  zeichnet den Null-Balken abhängig von der Skala und dem Typen des Diagramms (Balken, Kerzen oder Linie).
  9. CHistoryDraw –  erzeugt beim Benutzer den Eindruck, dass sich der letzte Abschluss in Echtzeit ändert.

* – diese Klassen verfügen über keine grafischen Objekte.

Wie ich bereits erwähnt habe, erstellen die Objekte der Klassen CChartData und SBase ein gemeinsames Datenfeld für alle Objekte innerhalb des Players mithilfe der Aggregation. Das Objekt der Klasse CChartData wird zum Speichern und Aktualisieren der Informationen über das Diagramm sowie dessen Verwaltung verwendet. Mit Verwaltung des Diagramms meinen wir die Veränderung seiner Einstellungen durch Kopieren der Einstellungen des Hauptdiagramms. Auf diese Art werden die Diagramme synchronisiert. Der Benutzer gibt nur das Startsignal durch eine Änderung der Einstellungen des aktiven Diagramms und dann führen mehrere Funktionen des Players die restlichen Synchronisierungsaktionen durch.

Dies wird folgendermaßen bewerkstelligt:

Die im Expert Advisor beschriebene Funktion CopyPlayer(n) ruft die Funktion CPlayer::Copy(CPlayer *base) in einer Schleife auf und übergibt dabei assoziativ den Pointer zum Player des aktiven Diagramms. Innerhalb von CPlayer::Copy(CPlayer *base) wird der Pointer des Objekts CChartData des aktiven Players aus dem Player-Pointer assoziativ übergeben. Somit werden die Informationen über den Status des aktiven Diagramms zum Kopieren im Objekt der CChartData-Klasse des untergeordneten Diagramms platziert. Anschließend werden die Informationen in der Funktion CPlayer::Update() aktualisiert. In dieser Funktion werden alle notwendigen Überprüfungen durchgeführt und alle Objekte in die erforderlichen Zustände umgeschaltet.

Ich hatte Ihnen versprochen, zu erzählen, wie Zeiträume in der Liste verfügbarer Zeiträume des Generators hinzugefügt werden. Öffnen Sie hierzu die Include-Datei "Player5_1.mqh". Das statische Array TFarray[] wird am Anfang der Datei deklariert. Ein erforderlicher Zeitraum sollte in der Aufzählung, die das Array befüllt, an seinem Bestimmungsort platziert werden. Und denken Sie daran, die Größe des Arrays in der Variable CountTF zu ändern. Kompilieren Sie anschließend den Expert Advisor Player History Trades exp v5.


Bilanz- und Drawdown-Diagramme

Der Bilanz-Indikator wird über das Objekt der CPlay-Klasse gesteuert. Hier befinden sich Methoden und Buttons für die Steuerung.

Die Methoden für die Steuerung des Indikators sind:

   Ind_Balance_Create();                 // add the indicator
   IndicatorDelete(int ind_total);     // delete the indicator
   EventIndicators(bool &prev_state);   // send an event to the indicator
   StateIndicators();                  // state of the indicator, state checks

Die Methoden zum Hinzufügen/Löschen funktionieren abhängig vom Zustand des Buttons name_on_balance. Sie nutzen die MQL5-Standardfunktionen IndicatorCreate() und ChartIndicatorDelete().

Der Indikator erhält ein Ereignis und führt Berechnungen durch, die sich abhängig vom Ereigniscode in der OnChartEvent()-Funktion des Indikators befinden. Ereignisse lassen sich in drei Typen einteilen. 

Diese sind "Aktualisieren des Indikators", "Berechnen der Gesamtbilanz" und "Berechnen der Bilanz für ein Symbol". Somit steuert der Benutzer beim Senden von Ereignissen abhängig vom Zustand des Buttons name_all_balance den Typ der Berechnung. Doch der Indikatorcode selbst beinhaltet kein Parsing der Handelshistorie, keine Berechnung der Position oder Neuberechnung des Gewinns. Das braucht der Indikator nicht.

Der Bilanz-Indikator dient der Darstellung historischer Daten, also ergibt es keinen Sinn, jedes Mal, wenn Sie den Typen ändern oder den Indikator hinzufügen/entfernen, alles erneut durchzuführen. Der Indikator liest die binäre Datendatei, die für den Timeframe M1 berechnet wurde und teilt die Daten anschließend abhängig vom aktuellen Timeframe des Diagramms auf.

Diese binäre Datei wird durch die in OnInit() aufgerufene Funktion Balance_Process() vorbereitet. Falls der Benutzer ein Symbol hinzufügt, das nicht für den Handel verwendet wurde, und keine entsprechende binäre Datei existiert, zeigt der Indikator die Historie der Gesamtbilanz in beiden Varianten an.

Sprechen wir nun über das Format der an den Indikator übergebenen Daten. Um die Informationen korrekt aufzuteilen, reicht es nicht, vier Punkte eines Balkens (Eröffnung, Maximum, Minimum und Schließung) zu kennen.

Sie müssen auch wissen, welcher Wert früher da war – Maximum oder Minimum. Zum Wiederherstellen dieser Information nutzt die Funktion Balance_Process() das gleiche Prinzip wie der "1-minütige OHLC"-Modus des Strategietesters: Wenn der Schließungspreis eines Balkens niedriger ist als der Eröffnungspreis, ist der zweite Punkt das Maximum, ansonsten ist er das Minimum.

Das gleiche Schema wird auch für den dritten Punkt verwendet. Als Ergebnis erhalten wir das Datenformat (Eröffnung, 2. Punkt, 3. Punkt, Schließung), in dem alles konsistent und eindeutig ist. Dieses Format wird für die Aufteilung der Historie von M1-Geboten verwendet. Das Ergebnis wird für die Berechnung der Historie von Bilanzen und Eigenkapital verwendet, abhängig von der geparsten Handelshistorie (im gleichen Format).


Fazit

Als Fazit möchte ich sagen, dass dieses Programm nicht als Visualizer des Testers dient, auch wenn wir es auf diese Weise nutzen können. Doch es würde mich freuen, wenn die darin umgesetzten Ideen sich für den echten Visualizer als nützlich erweisen. Die Entwicklung des Players soll Händlern und EA-Entwicklern bei der Vorbereitung auf die anstehende Meisterschaft und bei der harten Arbeit, die die Analyse von Handelsstrategien darstellt, helfen.

Zusätzlich möchte ich sagen, dass MQL5 ein mächtiges Programmierwerkzeug ist, das die Umsetzung riesiger Projekte ermöglicht. Falls Sie diesen Beitrag immer noch lesen, ist Ihnen vermutlich aufgefallen, dass das "Player"-Projekt aus fast 8000 Zeilen Code besteht. Ich könnte mir nicht vorstellen, einen solchen Code in MQL4 zu schreiben, und das Problem dabei liegt nicht darin, ob sich das alles mit Verfahren beschreiben lässt. Wenn es ein fertiges Programm gibt, lässt es sich in einen verfahrenszentrierten Stil umschreiben. Doch solche Projekte von Grund auf zu entwickeln, ist wirklich schwierig.

Viel Erfolg!