Kontinuierliche Walk-Forward-Optimierung (Teil 4): Optimierungsmanager (automatische Optimierung)

Andrey Azatskiy | 9 Juni, 2020

Einführung

Dies ist der nächste Artikel in der Reihe "Kontinuierliche Vorwärtsoptimierung". Hier stelle ich das erstellte Programm vor, das die programmierte automatische Optimierung umsetzt. In den vorhergehenden Artikeln wurden die Details der Programmimplementierung sowohl auf der Terminalseite als auch auf der Bibliotheksseite beschrieben, die zur Arbeit mit den erzeugten Optimierungsberichten verwendet wird. Sie können diese Artikel unter den untenstehenden Links einsehen:

  1. Kontinuierliche Walk-Forward-Optimierung (Teil 1): Arbeiten mit Optimierungsberichten
  2. Kontinuierliche Walk-Forward-Optimierung (Teil 2): Mechanismus zur Erstellung eines Optimierungsberichts für einen beliebigen Roboter
  3. Kontinuierliche Walk-Forward-Optimierung (Teil 3): Anpassen eines Roboters an die automatische Optimierung

Der vorliegende Artikel zeigt das allgemeine Bild des erstellten Produkts und dient als Anleitung. Die Funktionsweise des erstellten automatischen Optimierung kann erweitert werden. So kann der Algorithmus, den wir weiter besprechen werden, leicht durch Ihren eigenen Optimierungsalgorithmus ersetzt werden, sodass Sie jede gewünschte Idee umsetzen können. Die weitere Beschreibung legt auch die interne Struktur des erstellten Programms offen. Der Hauptzweck des Artikels ist es, den Mechanismus der Arbeit mit der resultierenden Anwendung und deren Fähigkeiten zu beschreiben. Tatsache ist, dass es meinen Kollegen manchmal schwer fiel, ihn schnell zu verstehen. Die automatische Optimierung ist jedoch recht einfach einzurichten und zu benutzen — und das werde ich weiter zeigen. Daher kann der Artikel als eine Gebrauchsanweisung für die Anwendung behandelt werden, die alle möglichen Fallstricke und Einrichtungsspezifika behandelt.


Beschreibung der Funktionsweise der automatischen Optimierung

Um mit der Analyse des erstellten Programms fortzufahren, müssen wir zunächst den Zweck dieses Projekts definieren. Wir entschieden uns für einen wissenschaftlichen Ansatz beim Handel und begannen mit der Schaffung klar programmierter Handelsalgorithmen (egal, ob wir es mit indikatorbasierten Robotern oder mit solchen zu tun haben, die Fuzzy-Logik und neuronale Netze anwenden — alle sind programmierte Algorithmen, die bestimmte Aufgaben ausführen). Daher sollte auch der Ansatz für die Auswahl der Optimierungsergebnisse formalisiert werden. Mit anderen Worten, wenn während der Verweigerung der Anwendung von Zufälligkeit im Handelsprozess, dann sollte auch der Prozess der Vorbereitung auf den Handel automatisiert werden. Andernfalls können wir die Ergebnisse, die uns gefallen, nach dem Zufallsprinzip auswählen, was näher an der Intuition als am Systemhandel liegt. Diese Idee ist das erste Motiv, das mich dazu bewogen hat, diese Anwendung zu schaffen. Das Nächste ist die Möglichkeit, Algorithmen zu testen, indem man sie optimiert — mit Hilfe der in der folgenden Abbildung gezeigten kontinuierlichen Walk-Forward Optimierung.   


Die kontinuierliche Walk-Forward-Optimierung wechselt zwischen historischen (gelb) und vorwärtsgerichteten (grün) Optimierungsdurchläufen in einem gegebenen Zeitintervall. Angenommen, Sie haben eine 10-jährige Historie. Wir bestimmen, dass die Optimierungsperiode aus einem Intervall von einem Jahr und einem Vorwärtsintervall aus einem Quartal (oder 3 Monaten) bestehen soll. Als Ergebnis haben wir ein Intervall von 1,25 Jahren (1 Jahr + 1 Quartal) für einen Optimierungsdurchgang + einen Vorwärtstest. In der Abbildung charakterisiert jede Zeile dieses Zeitintervall. 

Als Nächstes produzieren wir die gleiche Art von Prozess:

  1. Wir optimieren in der gewählten Historienperiode (1 Jahr).
  2. Wir wählen die besten Parameter unter den Ergebnissen im Optimierungsintervall aus, wobei wir eine feste Methode verwenden.
  3. Schalten wir in das Vorwärts-Zeitintervall und führen einen Test durch, kehren wir dann zum ersten Schritt zurück — jetzt auf einem verschobenen Intervall. Wiederholen wir diesen Vorgang, bis die aktuelle Zeitperiode erreicht ist.
  4. Nachdem wir die Testergebnisse jedes Quartals gesammelt haben, erhalten wir die endgültige Beurteilung der Lebensfähigkeit des Algorithmus.
  5. Der letzte Optimierungsdurchgang (für den es keinen Forward-Test gibt) kann zum Handel freigegeben werden.

Auf diese Weise erhalten wir eine Methode zur Prüfung der Nachhaltigkeit von Algorithmen durch Optimierung. In diesem Fall müssen wir die Algorithmen jedoch nach jedem Ablauf der Forward-Periode erneut optimieren. Mit anderen Worten, wir setzen ein bestimmtes Intervall für die erneute Optimierung des Algorithmus fest, legen die Methode für die Auswahl der Parameter fest und führen diese Prozedur zunächst an der Historie durch und wiederholen sie später jedes Mal, wenn die Handelsperiode, die der Forward-Testperiode entspricht, abläuft. 

Erstens erlaubt uns diese Optimierungstechnik, eine klar definierte Optimierungslogik zu haben, die es uns erlaubt, ein Ergebnis zu erhalten, das frei von menschlichen Eingriffen ist.

Zweitens erhalten wir ein vollständiges Bild der Stresstests des Algorithmus, indem wir die Optimierung für neue Vermögenswerte oder neue Algorithmen mit einer ähnlichen Technik durchführen. Wenn sowohl die Parameterauswahlmethode als auch der optimierte Parameter bei allen Vorwärtsoptimierungen fest und unverändert sind, erhalten wir einen kontinuierlichen Stresstest, mit dem wir feststellen können, ob der Markt für unsere Strategie nicht mehr geeignet ist.

Drittens erhalten wir eine Menge von Optimierungsrahmen innerhalb eines recht kleinen Zeitraums, was die Zuverlässigkeit der durchgeführten Tests erhöht. Bei der oben erwähnten Unterteilung in eine 1-Jahres-Optimierung und eine 1-Viertel-Vorwärts-Optimierung bietet beispielsweise ein 2-Jahres-Intervall 4 Stresstests und eine abschließende Optimierung.


Starteinstellung der Optimierung

Nachdem wir nun den Prozess, den der Antrag ausführt, besprochen haben, wollen wir seine Verwendung betrachten. Diese Artikelserie ist eine logische Fortsetzung meiner früheren Artikel über die grafische Schnittstelle für die Verwaltung des Optimierungsprozesses:

  1. Optimierungsmanagement (Teil I)
  2. Optimierungsmanagement (Teil II)

In den Artikeln wird beschrieben, wie eine Optimierung oder ein Test im Terminal als kontrollierter Prozess durchgeführt werden kann. Die gleiche Methode wird in dieser Serie verwendet. Einer der grundlegenden Unterschiede besteht jedoch darin, dass der Steuerungsprozess nicht als Zusatz zum Terminal, sondern als eigenständiges Programm implementiert wird. Dieser Ansatz ermöglicht es, alle auf dem Computer installierten Terminals zu verwenden. In der vorhergehenden Artikelserie haben wir eine Erweiterung geschaffen, die von einem Arbeitsterminal aus gestartet werden konnte. Die Erweiterung könnte alle auf dem Computer installierten Terminals zusätzlich zu dem Terminal nutzen, von dem aus sie gestartet wurde. Die aktuelle Anwendung hat ebenfalls Zugriff auf alle auf dem Computer installierten Terminals, aber sie funktioniert jeweils nur mit einem Terminal und kann jedes beliebige Terminal starten.

Um den erfolgreichen Betrieb des Auto-Optimierers zu gewährleisten, stellen Sie sicher, dass das ausgewählte Terminal geschlossen ist, bevor Sie die Anwendung starten. 

Die Anwendung funktioniert wie folgt:

  1. Beim nächsten Prozessstart, unabhängig davon, ob es sich um eine Optimierung oder einen Test handelt, startet die Anwendung ein Terminal und delegiert den gesamten Testprozess an das Terminal. Sobald der Test oder die Optimierung abgeschlossen ist, fährt das Terminal herunter.
  2. Nach dem Test oder der Optimierung erstellt der Roboter einen Bericht mit den Optimierungsergebnissen (für weitere Details lesen Sie bitte die vorherigen Artikel innerhalb dieser Serie).
  3. Der Auto-Optimierer weiß, wo sich der Bericht befindet, also liest und verarbeitet er den Bericht. Sobald die Verarbeitung abgeschlossen ist, wird entweder eine neue Optimierungs- und Testphase gestartet, oder die Optimierung wird abgeschlossen und die Ergebnisse werden in der Registerkarte "Ergebnis" angezeigt. 

Der implementierte Optimierungsmechanismus wird später betrachtet, während der Zweck des aktuellen Teils darin besteht, den Programmablauf mit einem Minimum an technischen Details zu beschreiben. Vergessen Sie nicht, dass Sie Ihren eigenen Algorithmus in den für die Optimierung verantwortlichen Codeteil einfügen können. Der Auto-Optimierer ist ein Steuerprogramm, das Optimierungsprozesse startet, aber die Logik dieser Prozesse kann unterschiedlich sein. Hier ist ein Screenshot der Hauptregisterkarte der resultierenden Anwendung.


Wenn Sie sich den Screenshot genauer ansehen, bietet die erste ComboBox namens "Select Optimiser", die sich im Bereich grün befindet, die Auswahl der im Programm implementierten Optimierungstypen. 

Die grafische Benutzeroberfläche der Anwendung ist in 2 Hauptregisterkarten unterteilt. Die Hauptregisterkarte zeigt die Einstellungen. Hier fangen wir an zu arbeiten, wenn wir eine Optimierung starten oder stoppen müssen. Die Registerkarte wurde im zweiten Teil ausführlich beschrieben. Die andere Registerkarte "Ergebnis" ist die Registerkarte, auf der die Optimierungsergebnisse angezeigt werden.

Zuerst müssen wir beim Start des Auto-Optimierers das zu verwendende Terminal auswählen. Das Prinzip der Terminalauswahl ist dasselbe wie in der vorherigen grafischen Oberfläche, die für den Optimierungsstart verwendet wurde. Mit anderen Worten, der Auto-Optimierer findet alle auf diesem Computer installierten Terminals (jedoch nur diejenigen, die eine Standard-Installation verwenden, und nicht eine portable). 

Die Registerkarte "Einstellungen" ist in 4 Abschnitte unterteilt. Die Ränder jedes Bildschirmteils können gezogen werden. Dies ist besonders praktisch, wenn Sie mit einem Algorithmus mit vielen Eingabeparametern arbeiten.

Außerdem werden wir aufgrund des Textformats von Mengendateien nicht in der Lage sein, zwischen den Formaten der Algorithmusparameter zu unterscheiden. Beispielsweise werden alle Enum-Parameter als int. Aus diesem Grund wurde beschlossen, die Liste der Parameter als Zeichenfolgen im zweiten Teil des Bildschirms anzuzeigen. Wenn es für Sie nicht bequem ist, Optimierungsschritte und andere Roboterparameter direkt im Auto-Optimierer zu konfigurieren, können Sie alle erforderlichen Einstellungen im Terminal vornehmen. Nach dem Wechseln der Registerkarten im Tester (oder nach dem Schließen des Terminals) werden die Einstellungen dann in die gewünschte Datei geschrieben. Sie brauchen nur den gewünschten Algorithmus im Auto-Optimierer auszuwählen — er wird sofort mit Ihren Einstellungen geladen.

Die Einstellung der folgenden Felder ist für den Start einer Optimierung oder eines Tests aus dem Auto-Optimierer erforderlich:

  1. Wählen eines EAs.
  2. Überprüfen Sie die zu optimierenden Parameter und wählen Sie den Bereich aus (wie im Terminal).
  3. Wählen Sie mindestens ein Sortierkriterium aus: dies beeinflusst die Sortierung der vom Roboter erzeugten Daten, und das beste der Ergebnisse wird im Vorwärtsintervall gestartet (kann für einen Testlauf weggelassen werden).
  4. Wählen Sie die Daten für die Vorwärtsoptimierung (oder die Testdaten, wenn Sie einen Testlauf durchführen).   
  5. Wählen Sie den gewünschten Optimierer aus, wenn Sie die Optimierung ausführen ("Select Optimiser:" drop-down Liste). 
  6. Geben Sie "Asset name" an — den Namen des Symbols, für das die angeforderte Operation ausgeführt werden soll. Im Falle eines Fehlers ist das Terminal nicht in der Lage, den Test oder die Optimierung auszuführen.
  7. Sie können "Directory prefix" für die zusätzliche Angabe des Namens des zu speichernden Optimierungsdurchgangs verwenden. Ein Ordner mit den Optimierungsergebnissen wird nach dem Ende der Optimierung in einem speziellen internen Programmverzeichnis angelegt. Der Ordnername wird wie folgt festgelegt: "{Verzeichnis-Präfix} {Ausgewählter Optimierer} {Expertenname} {Assetname}". Dies sind die in der Liste "Optimisation:" angezeigten Namen, von denen sie zur Ansicht und weiteren Analyse hochgeladen werden können.
  8. Die Dropdown-Liste mit den Parametern "Rewrite" oder "Append" ist ebenfalls optional. Sie legt die Aktion fest, die der automatische Optimierer ausführen soll, wenn er unter den gespeicherten Dateien Ergebnisse mit demselben Namen findet. Wenn "Rewrite" gewählt wird, werden alle Dateien mit neuen Dateien neu geschrieben. Wenn "Append" gewählt wird, werden die übereinstimmenden Optimierungsdaten überschrieben. Wenn dasselbe Intervall in der aktuellen Liste der Optimierungsintervalle und in der Liste der zuvor gespeicherten Intervalle gefunden wird, werden die gespeicherten Ergebnisse mit den neuen überschrieben. Wenn die Intervalle neu sind, werden sie zu den bestehenden hinzugefügt.

Sobald das Setup durchgeführt ist, klicken Sie auf "Start/Stop", um den Prozess zu starten. Ein wiederholtes Klicken auf diese Schaltfläche unterbricht den Optimierungsprozess. Während des Optimierungsprozesses wird sein Status in der Fortschrittsanzeige und in der Textbeschriftung angezeigt, die sich am unteren Rand des Auto-Optimierungsfensters befinden. Nach dem Ende der Optimierung werden die Optimierungsergebnisse auf die Registerkarte "Result" hochgeladen und die Fortschrittsanzeige wird auf den Anfangszustand zurückgesetzt. Wenn Sie jedoch einen Test ausführen, indem Sie auf "Start / Stop" klicken, wird das Terminal nicht automatisch geschlossen. Dies geschieht der Bequemlichkeit halber und erlaubt es dem Nutzer, alle erforderlichen Daten zu untersuchen. Sobald Sie die erforderlichen Daten untersucht haben, schließen Sie das Terminal manuell, um den automatischen Optimierungsvorgang fortzusetzen. Bitte vergessen Sie nicht, dass das Terminal immer geschlossen sein sollte, da die Anwendung in der Lage sein muss, das Terminal selbständig zu verwalten.

Außerdem sollten Sie den Optimierer selbst konfigurieren, bevor Sie Optimierungen starten. Dies ist keine zwingende Voraussetzung, aber die Struktur des Optimierungsmanagers ermöglicht die Erstellung eines nutzerdefinierten Optimierers sowie die Einstellung individueller Parameter für jeden dieser Optimierer. Einstellungen können durch Klicken auf die Schaltfläche "GUI" neben dem Kombinationsfeld des Optimierer-Auswahlschalters geöffnet werden. Die Einstellungen des implementierten Optimierers sind wie folgt:

  1. Test on Ticks — gibt die Testmethode der Daten mit historischen und denen des Vorwärtstests an. Die Optimierungsmethode wird im ersten Teil des Fensters mit den Einstellungen angegeben, während die Testmethode in den Einstellungen des Optimierers angegeben wird. Wenn die Option aktiviert ist, werden die Tests mit Hilfe von Ticks durchgeführt. Wenn sie deaktiviert ist, wird der Test im Modus 1 Minute OHLC durchgeführt. 
  2. Replace real dates to setted — legt fest, ob reale Start- und Enddaten der Optimierung durch die übergebenen Daten ersetzt werden sollen. Die Start- und Endzeit der Optimierung wird unter Verwendung eines 1-Minuten-Zeitrahmens gespeichert. Manchmal, wenn kein Handel stattfindet oder es sich um einen Feiertag handelt, können die tatsächlichen Start- und Enddaten von den angegebenen abweichen. Wenn diese Option aktiviert ist, setzt der Optimierer bekanntere Daten und weiß mit Sicherheit, zu welchem Intervall die Ergebnisse gehören. Wenn Sie jedoch reale Handelsdaten sehen möchten, deaktivieren Sie diese Option.
  3. Use different shift for tick test — wir haben im zweiten Artikel besprochen, dass Slippage und Shift zu den Ergebnissen hinzugefügt werden können. Wenn Ticks zum Testen verwendet werden, kann Slippage deaktiviert oder vollständig reduziert werden. Diese Option wurde genau für diesen Fall hinzugefügt. Sie ist nur aktiviert, wenn "Test on Ticks" aktiviert ist. Um die Option zu verwenden, geben Sie die Algorithmusparameter an, die für die Angabe von Kommission und Schlupf verantwortlich sind, und setzen Sie ihnen neue Werte zu. Indem Sie den für den Schlupf verantwortlichen Roboterparameter angeben und auf 0 setzen, können Sie den Schlupf aus den Ergebnissen im Ticktestmodus entfernen. Nachdem Sie einen Parameter und seinen Wert angegeben haben, fügen Sie diesen Parameter über den Parameter "Add" in die Tabelle ein, um diesen Parameter zu speichern.

Es ist nicht notwendig, die eingegebenen Parameter zu speichern (es gibt keine Schaltfläche "Speichern"), da sie automatisch gespeichert werden. Schliessen Sie dieses Fenster vor dem Start der Optimierung, um ein versehentliches Ändern der Parameter während des Optimierungsprozesses zu verhindern. 


Arbeit mit den Optimierungsergebnissen

Greifen Sie nach dem Start der Optimierung nicht in den Prozess ein. Entfernen Sie den EA auch nicht aus dem Chart, bis der Optimierer ihn stoppt. Andernfalls betrachtet der Optimierer diese Situation als Fehler, da die Daten nicht übereinstimmen werden. Sobald der Prozess abgeschlossen ist und das Terminal geschlossen wird, lädt der Optimierer den Optimierungsbericht auf die Registerkarte Results hoch, von wo aus Sie die geleistete Arbeit auswerten können. Die Registerkartenstruktur ist in der folgenden Registerkarte dargestellt:

 

Die Registerkarte Ergebnisse ist ebenfalls in Teile unterteilt, die zur leichteren Erklärung mit Zahlen gekennzeichnet sind. Der erste Teil enthält Optimierungsdurchgänge, die auf Registerkarten aufgeteilt sind (Ausgewählter Durchgang und Optimierungen). Die erste Container-Registerkarte mit dem Namen "Selected pass" enthält ausgewählte Optimierungsdurchgänge. Diese Durchläufe sind in zwei Registerkarten unterteilt ("Forward" und "History"). Sehen wir uns an, wie die Optimierungsparameter zwischen diesen Registern verteilt sind. Beispielsweise werden die folgenden Daten angegeben:

  1. 01.01.2012 - 07.12.2012 - History
  2. 10.12.2012 - 07.03.2012 - Forward

Die Optimierung wird im historischen Zeitraum durchgeführt. Dann werden die besten Parameter durch Filterung und Sortierung ausgewählt (siehe Beschreibung im vorigen Kapitel). Nach der Auswahl werden zwei Tests durchgeführt: einer auf dem historischen und der andere auf dem Vorwärtsintervall. Ferner werden die Testergebnisse der besten Parameter zur Registerkarte "Selected pass" hinzugefügt, wo sie auf die Registerkarten "History" und "Forward" aufgeteilt werden. Die Optimierungsdurchgänge werden der Registerkarte "Optimisations" hinzugefügt, wo Sie die gesamte Liste der Optimierungen für jedes historische Intervall einsehen können. Betrachten Sie die Registerkarte "Optimisations" im Detail.


Die Tabellenstruktur (der erste Teil der Registerkarte "Optimisations") ähnelt der Struktur der Registerkarten "Forward" und "History". Aber diese Tabelle zeigt alle Optimierungsdurchgänge innerhalb des angeforderten Intervalls. Das gewünschte Zeitintervall kann aus der Combobox "Optimisation dates" ausgewählt werden. Wenn ein neues Intervall ausgewählt wird, wird die gesamte Tabelle mit den Optimierungsdurchläufen aktualisiert. Der zweite Teil der Registerkarte ist ähnlich wie der dritte Teil des Fensters "Settings", und alle Änderungen in diesen Registerkarten werden synchronisiert.

Sowohl die Registerkarten "Optimisations" als auch "Selected pass" enthalten die Schaltfläche "Save to (*.csv)". Wenn diese Schaltfläche für "Selected pass" angeklickt wird, wird eine *.csv-Datei erstellt, die die Liste der historischen und Vorwärtsoptimierungsdurchgänge enthält. Wenn auf die Schaltfläche für "Optimisations" geklickt wird, werden Informationen über alle durchgeführten Optimierungen in die entsprechende *.csv-Datei heruntergeladen. Die Schaltflächen "Sort" und "Filter" sind nur in der Registerkarte "Optimisations" verfügbar. Ihr Zweck ist es, die Filterung und Sortierung der resultierenden Optimierungsdurchgänge in Übereinstimmung mit den im zweiten Teil der Registerkarte "Optimisations" festgelegten Einstellungen zu ermöglichen. In der Praxis ist diese Option nicht erforderlich, da die automatische Optimierung den gleichen Mechanismus verwendet. Die Option erlaubt jedoch das Verwenden jeder gewünschten benutzerdefinierten Filterung. 

Beide Tabellen sind interaktiv. Ein einziger Klick auf eine Tabellenzeile aktualisiert den zweiten und dritten Teil der Registerkarte "Result" entsprechend dem gewählten Optimierungsdurchgang. Ein Doppelklick startet einen Test im Terminal. In diesem Fall wird das Terminal nach Beendigung des Tests nicht geschlossen. Sie sollten es also nach dem Studium der Ergebnisse manuell schließen. Einige Testerparameter können vor dem Start eines Tests konfiguriert werden: der zweite Teil der Registerkarte "Results".

  

Testbeginn- und -enddatum werden automatisch in Übereinstimmung mit dem gewählten Zeitintervall aktualisiert. Sie können jedoch durch einen Doppelklick auf die gewünschte Zeile geändert werden. Sie können auch Ausführungsverzögerung und testing type (ticks, OHLC etc.) auswählen. Standardmässig sind "Keine Latenz, ideale Ausführung" und "Jeder Tick" eingestellt. 

Der dritte Teil der Registerkarte zeigt die Handelsstatistik für den ausgewählten Optimierungsdurchgang (Täglicher PL und Max PL/DD) und die Roboterparameter für den ausgewählten Durchgang (Registerkarte "Bot-Parameter"). Die Registerkarte "Max PL/DD" zeigt nicht den endgültigen Gewinn-/Verlustwert, sondern nur den Gesamtgewinn (die Summe aller gewinnbringenden Positionen) und den Gesamtverlust (die Summe aller verlierenden Positionen). Gewinn und maximaler Drawdown, die zum Zeitpunkt des Handelsabschlusses registriert wurden, werden in der Tabelle mit den Optimierungen und dem Testergebnis angezeigt. Die Registerkarte Daily PL zeigt den durchschnittlichen Tagesgewinn und den durchschnittlichen Tagesverlust, ähnlich wie der im Terminal verfügbare Bericht.

Ein weiterer, einfachster Algorithmus für die Arbeit mit dem automatischen Optimierer

Betrachten wir das fertige Beispiel eines Roboters, der mit dem automatischen Optimierer arbeitet. Wir haben die Algorithmusvorlage bereits im dritten Artikel dieser Serie betrachtet. Nun wollen wir die Vorlage für einen Algorithmus im C-Stil anpassen. Zunächst betrachten wir den Algorithmus selbst. Es handelt sich um einen Algorithmus mit 2 gleitenden Durchschnitten. Die Positionen werden entweder durch einen festen Stop-Loss oder durch einen festen Take-Profit geschlossen. Die Implementierung von Funktionen, die die EA-Logik beschreiben, werden aus dem untenstehenden Code entfernt, da dies nicht der Zweck des Beispiels ist.

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

#include <Trade/Trade.mqh>
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not enabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

//+------------------------------------------------------------------+
//| Calculate stop                                                   |
//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Calculate take                                                   |
//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Open position according to direction                             |
//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Get direction                                                    |
//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
//+------------------------------------------------------------------+

Dieser EA-Codeteil ist grundlegend. Er wird benötigt, um die Änderungen zu analysieren, die wir produzieren müssen, um den EA im automatischen Optimierer verfügbar zu machen. 

Bitte beachten Sie, dass für die Aufgabe kein effizienter EA erforderlich war, sodass dieser Roboter höchstwahrscheinlich Verluste erzeugt. Der EA hat eine Einschränkung, um seinen versehentlichen Start im realen Handel zu vermeiden. Um die Beschränkung aufzuheben (und um ihn im Handel starten zu können), kommentieren Sie die Definition von TESTER_ONLY

In OnOnit instantiieren wir die Indikatoren des gleitenden Durchschnitts. Dementsprechend sollten die Indikatoren in OnDeinit gelöscht werden. Zur Bestimmung der Richtung wird die im Code deklarierte Enumeration Direction verwendet. Die Positionen werden durch die Klasse CTrade in der Funktion open_position geöffnet. Die gesamte Logik wird in vier Codezeilen im OnTick geschrieben. Fügen wir nun den Anschluss der erforderlichen Funktionalität an den Roboter hinzu.

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

#include <Trade/Trade.mqh>
#include <History manager/AutoLoader.mqh> // Include CAutoUploader
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

// Comission and price shift (Article 2) 
input double _comission_ = 0; // Comission
input int _shift_ = 0; // Shift

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;
CAutoUploader * auto_optimiser; // Pointer to CAutoUploader class (Article 3)
CCCM _comission_manager_; // Comission manager (Article 2)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not enabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }

   // Set Commission and shift
   _comission_manager_.add(_Symbol,_comission_,_shift_);

   // Add robot params
   BotParams params[];
   APPEND_BOT_PARAM(ma_fast,params);
   APPEND_BOT_PARAM(ma_slow,params);
   APPEND_BOT_PARAM(_sl_,params);
   APPEND_BOT_PARAM(_tp_,params);
   APPEND_BOT_PARAM(_lot_,params);
   APPEND_BOT_PARAM(_comission_,params);
   APPEND_BOT_PARAM(_shift_,params);

   // Add Instance CAutoUploader class (Article3)
   auto_optimiser = new CAutoUploader(&_comission_manager_,"SimpleMAMutex",params);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);

   // Delete CAutoUploaderclass (Article 3)
   delete auto_optimiser; 
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

//+------------------------------------------------------------------+
//| Calculate stop                                                   |
//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Calculate take                                                   |
//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Open position according to direction                             |
//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...   
  }

//+------------------------------------------------------------------+
//| Get direction                                                    |
//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   auto_optimiser.OnTick(); // Save current date (Article 3)

   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
//+------------------------------------------------------------------+

Alle Neuzugänge sind grün markiert. Betrachten wir sie in der Reihenfolge ihres Erscheinens. Zuerst binden wir die Header-Datei AutoLoader ein, die in Artikel 3 beschrieben wurde. Diese Datei enthält die Klasse CAutoUploader, deren Aufgabe es ist, die akkumulierte Handelshistorie herunterzuladen. In OnInit fügen wir der entsprechenden Klasse CCCM, die in Artikel 2 beschrieben wurde, eine Provision hinzu. Außerdem instanziieren wir die Klasse CAutoUploader, nachdem wir ihr die EA-Parameter hinzugefügt haben. Die Instanz der Klasse CAutoUploader wird in OnDeinit gelöscht, der den Aufruf eines Destruktors initialisiert, in dem der Handelsbericht in einer xml-Datei gespeichert wird (Artikel 1). 

Die EA-Logik bleibt unverändert, mit Ausnahme von OnTick. Dort rufen wir die Methode OnTick der Klasse CAutoUploader auf. Der Aufruf ermöglicht das korrekte Speichern von Testbeginn und -ende. Die Klasse CAutoUploader funktioniert nur im Tester und führt im realen Handel keine Aktionen aus. 


Schlussfolgerung

Der vorliegende Artikel beschreibt die Funktionalität des erstellten Optimierungsmanagers. Wie bereits zu Beginn dieses Artikels erwähnt, sollte der Artikel als eine Art Anleitung für die resultierende Anwendung behandelt werden. Technische Aspekte der Anwendungsimplementierung werden in weiteren Artikeln beschrieben. Das Folgende ist dem Artikel beigefügt:

Um das Programm der automatischen Optimierung auszuführen, kompilieren Sie es mit der Visual Studio IDE. Bitte beachten Sie, dass das Verzeichnis MQL5/Include/Libraries die Bibliothek "ReportManager.dll" enthalten sollte, die im ersten Artikel beschrieben wurde. Sie ist auch im beigefügten Projekt der automatischen Optimierung verfügbar (Kompilierung ist erforderlich).