English Русский 中文 Español 日本語 Português
So können Sie Ihre eigenen Optimierungskriterien implementieren

So können Sie Ihre eigenen Optimierungskriterien implementieren

MetaTrader 4Tester | 9 Februar 2016, 07:03
1 080 0
Nikolai Shevchuk
Nikolai Shevchuk

Einführung

Von Zeit zu Zeit hören wir die Meinung, dass es nötig sei, die Anzahl der Optimierungskriterien im MT4-Tester zu erhöhen. Aber Sie können sich sicherlich denken, dass es keine Rolle spielt, welche Kriterien die Entwickler auch hinzufügen, denn es wird immer Trader geben, die andere Kriterien benötigen. Kann man dieses Problem innerhalb von MQL4 und MetaTrader lösen? Ja, das ist möglich. Dieser Artikel zeigt, wie man einen individuell entworfenes Optimierungskriterium implementieren kann hierzu benutzen wir das Beispiel des standardisierten Expert Advisors des Gleitenden Durchschnittes. Das Kriterium steht hierbei in Relation zum Gewinn/Verlust.

Expert Advisor

Lassen Sie uns mit dem Optimierungskriterium beginnen. Für seine Berechnung müssen wir während der Testphase den maximalen Kontostand und den Verlust bestimmen. Um nicht auf die Logik der Expert Advisor-Operation angewiesen zu sein, werden wir die bereits existierenden Code-Strings ganz zu Anfang der Startfunktion hinzufügen ().

   if (AccountEquity() > MaxEqu) MaxEqu = AccountEquity();
   if (MaxEqu-AccountEquity() > MaxDD) MaxDD = MaxEqu-AccountEquity();

Um den letzten Tick zu berechnen, sollten Sie in deinit() repliziert werden. Danach können wir den Wert des Optimierungskriteriums berechnen

    Criterion = (AccountBalance()-StartBalance)/MaxDD;

Jetzt können wir mit dem Hauptteil beginnen: Die Überarbeitung des Optimierungsprozesses. Wir haben aber zunächst ein Problem: MQL4 verfügt über keinerlei eingebaute Mittel, um das Ende des Optimierungsprozesses zu bestimmen. Der einzige Ausweg aus diesem Dilemma, der mir bekannt ist, ist die sogenannte " Optimierung über einen Zähler ". Die Bedeutung hiervon ist die Folgende: Der einzige veränderbare Parameter des Expert Advisors ist der spezielle externe Variablenzähler. Hierbei gibt es aber immer noch eine schwerwiegende Konsequenz: Wir verlieren damit die Möglichkeit, die realen Parameter des Expert Advisors in einer standardisierten Art und Weise zu verändern, und wir müssen dies also selbst erledigen. Ein weiterer Nachteil besteht darin, dass der Optimierungs-Cache sich von unserem Freund in einen Feind verwandelt. Aber der Zweck rechtfertigt die Mittel, und daher wollen wir weitermachen.

Fügen wir also die externen Variablen hinzu :

extern int Counter                    = 1;    // Counter of the tester's running
extern int TestsNumber                = 200;  // he check digit - total number of runs 
extern int MovingPeriodStepsNumber    = 20;   // Number of optimization steps for MovingPeriod 
extern int MovingShiftStepsNumber     = 10;   // Number of optimization steps for MovingShift
extern double MovingPeriodLow         = 150;  // Lower limit of the optimization range for MovingPeriod
extern double MovingShiftLow          = 1;    // Lower limit of the optimization range for MovingShift
extern double MovingPeriodStep        = 1;    // Optimization step for MovingPeriod 
extern double MovingShiftStep         = 1;    // Optimization step for MovingShift

Als erstes kommt der Zähler. Die nächste Variable ist der Check Eins (und die Informationen). Dann ordnen wir die Anzahl der Schritte, das untere Limit und die Optimierungsschritte für die zwei eingebauten Variablen des Gleitenden Durchschnittes zu, den wir dadurch optimieren wollen. Sie können hier einige Unverhältnismögligkeit sehen: Wenn wir die vollständige Analyse durchführen möchten (und wir werden genau das tun), dann sollte das Produkt von MovingPeriodStepsNumber und MovingShiftStepsNumber identisch sein mit der TestsNumber.

Nach jedem Testlauf beendet der Expert Advisor seine Arbeit vollständig, und der nächste Durchgang kann als eine Reinkarnation des vorhergehenden Testlaufs angesehen werden. Wir verfügen über zwei Möglichkeiten zur Organisation " der genetischen Datenbank ": Globale Variablen und eine separate Datei. Wir werden beide benutzen.

Lassen Sie uns die Funktion init() modifizieren:

int init() {
  if (IsTesting() && TestsNumber > 0) {
    if (GlobalVariableCheck("FilePtr")==false || Counter == 1) {
      FilePtr = 0; 
      GlobalVariableSet("FilePtr",0); 
    } else {
      FilePtr = GlobalVariableGet("FilePtr"); 
    }
    MovingPeriod = MovingPeriodLow+((Counter-1)/MovingShiftStepsNumber)*MovingPeriodStep;
    MovingShift = MovingShiftLow+((Counter-1)%MovingShiftStepsNumber)*MovingShiftStep;
    StartBalance = AccountBalance();
    MaxEqu = 0;
    MaxDD = 0;
  }   
  return(0);
}

Unser hinzugefügter Code befindet sich innerhalb der Operationskonditionen lediglich im Tester und bei der Nicht-Null TestsNumber. Daher wird der Task TestsNumber=0 den Expert Advisor in einen standardisierten Gleitenden Durchschnitt (Englisch: "Moving Average") verwandeln. Während wir den Prozess der Optimierungen diskutieren, müssen wir jede Möglichkeit nutzen, um den Prozess schneller ablaufen zu lassen. Das ist auch der Grund dafür, weshalb der Code mit der Bedingung startet, einen through (durch die Testläufe hindurch) file pointer zu verwenden, unter der Benutzung einer globalen Variable. Dann kalkulieren wir die Werte der veränderbaren Parameter und initialisieren die Variablen, die für die Kalkulation der Optimierungskriterien benötigt werden.

Die Hauptarbeit sollte in der Funktion deinit() erledigt werden. Sobald wir die Testresultate haben, werden wir die Werte davon in einer Textdatei für das Optimierungskriterium, die Werte der optimierten Parameter und die Anzahl der Testläufe abspeichern. Nach dem Ende der Optimierung werden diese Ergebnisse dann sortiert werden, in Abhängigkeit vom Optimierungskriterium, und sie werden daraufhin in derselben Datei abgespeichert werden. Wir müssen hierzu also drei Situationen durchspielen: Der erste Start, der letzte Start, und alle anderen. Lassen Sie uns den Testlauf-Zähler benutzen, um diese voneinander zu trennen.
Die Bearbeitung des ersten Starts :

    if (Counter == 1) {
// First run, create/initialize a datafile.
      h=FileOpen("test.txt",FILE_CSV|FILE_WRITE,';');
      FileWrite(h,Criterion,MovingPeriod,MovingShift,Counter);
// Remember the position of the file pointer after writing in the global variable
      FilePtr = FileTell(h); 
      GlobalVariableSet("FilePtr",FilePtr);
      FileClose(h);

Die Besonderheit bei der Bearbeitung anderer Starts besteht darin, dass die neuen Daten in die Datei hinzugefügt werden:

    } else {
//  After the first start is processed, the data are added into the file
      h=FileOpen("test.txt",FILE_CSV|FILE_READ|FILE_WRITE,';');
//  It is time to use the file pointer written in the global variable
      FilePtr = GlobalVariableGet("FilePtr");
      FileSeek(h,FilePtr, SEEK_SET);
      FileWrite(h,Criterion,MovingPeriod,MovingShift,Counter);
//  Remember the file pointer position once again
      FilePtr = FileTell(h); 
      GlobalVariableSet("FilePtr",FilePtr);

Lassen Sie uns jetzt den letzten Start durchspielen:

      if (Counter == TestsNumber) {
        ArrayResize(Data,TestsNumber); 
// Returns the file pointer to the beginning       
        FileSeek(h,0,SEEK_SET);
// Read from the file the results of all testings
        int i = 0;
        while (i<TestsNumber && FileIsEnding(h)== false) {
          for (int j=0;j<4;j++) {
            Data[i][j]=FileReadNumber(h); 
          }
          i++;
        } 
// Sort the array according to our optimization criterion
        ArraySort(Data,WHOLE_ARRAY,0,MODE_DESCEND);
// Now let us arrange the results. Reopen the file        
        FileClose(h); 
        h=FileOpen("test.txt",FILE_CSV|FILE_WRITE,' ');
        FileWrite(h,"  Criterion","     MovingPeriod"," MovingShift"," Counter");
        for (i=0;i<TestsNumber;i++) {
          FileWrite(h,DoubleToStr(Data[i][0],10),"        ",Data[i][1],"        ",Data[i][2],"        ",Data[i][3]);
        }

Die Matrix wurde vorläufig initialisiert als eine doppelte Data[][4]. Und das ist auch schon alles. Lassen Sie uns nun aufräumen:

        GlobalVariableDel("FilePtr");
      }
      FileClose(h); 
    }
  }

Kompilieren, den Tester öffnen und Auswählen des Expert Advisors. Öffnen Sie dann die Eigenschaften-Seite des Expert Advisors, und überprüfen Sie vier Positionen:

-Das Produkt von MovingPeriodStepsNumber und MovingShiftStepsNumber MUSS identisch sein mit der TestsNumber.
- Die Optimierung darf NUR für den Zähler durchgeführt werden,
- Die Optimierungsspanne MUSS von 1 bis hin zu TestsNumber reichen, mit einer Schrittgröße von 1.
- Der genetische Algorithmus muss deaktiviert werden.

Starten Sie die Optimierung. Nach dem Abschluss gehen Sie zum Ordner [Meta Trader]\tester\files und sehen sich die Resultate in der Datei test.txt an. Der Autor hat diesen Prozess für EURUSD_H1 von der Mitte des Jahres 2004 ab für die Eröffnungspreise durchgeführt, und er hat die folgenden Ergebnisse erhalten :

Und jetzt werde ich Ihnen erklären, weshalb ich oben das Statement abgegeben habe, dass der Cache unser Feind sei. Die Sache ist nämlich die, dass, wenn wir die Testergebnisse aus dem Cache entnehmen, die Funktionen init() und deinit() nicht gestartet werden. Infolgedessen sind beim Neustart der Optimierung alle Varianten oder Teile unserer Varianten nicht berücksichtigt. Darüber hinaus gilt, dass die tatsächliche Anzahl der Testläufe geringer sein wird als die TestsNumber, und die Matrix Data wird daher einige Nullen enthalten. Der Autor schlägt zwei Lösungsansätze vor, um den " Cache-Effekt " zu eliminieren: Die Neukompilierung eines Expert Advisors, oder die Schließung/Pausierung/erneute Öffnung des Testerfensters.
Die Cache-Interferenz kann entdeckt werden, indem man eine unabhängige Zählung der Testläufe vornimmt. Es gibt drei kommentierte Einfügungen für die Organisation eines solchen Zählers, bei dem eine spezielle globale Variable zum Einsatz kommt, wie man sie beispielhaft in dem angefügten EA-Code nachlesen kann

// Code of the independent counter
    if (GlobalVariableCheck("TestsCnt")==false || Counter == 1) {
      TestsCnt = 0; 
      GlobalVariableSet("TestsCnt",0); 
    } else {
      TestsCnt = GlobalVariableGet("TestsCnt"); 
    }
 
// Code of the independent counter
    TestsCnt++;
    GlobalVariableSet("TestsCnt",TestsCnt); 
 
// Code of the independent counter
        GlobalVariableDel("TestsCnt");

Und noch eine Schlussbemerkung: Der aufmerksame Leser hat mit Sicherheit die Tatsache entdeckt, dass wir auch auf die Variable FilePtr (und die damit einhergehende globale Variable) verzichten können die Daten werden am Ende der Datei eingefügt, und sie werden vom Anfang gelesen. Wofür dienen sie also? Die Antwort ist die folgende: Dieser Expert Advisor ist für die Demonstration der Optimierungs-/Wartungsmethode gedacht. Diese Methode erlaubt es, die Operation im fliegenden Betrieb durchzuführen, zusammen mit den Resultaten von vorhergehenden Testlä#252;r Tasks, die es erforderlich machen, mit vorherigen Resultaten im fliegenden Betrieb zu arbeiten, können wir beispielsweise das Management von out-of-sample-Testläufen anführen, sowie die Implementierung von selbsterstellten genetischen Algorithmen.

Fazit

Der Grund für das besondere Interesse an diesem Problem waren Themen aus dem Forum http://forum.mql4.com.


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

Beigefügte Dateien |
Der Objekt-Ansatz in MQL Der Objekt-Ansatz in MQL
Dieser Artikel wird in erster Linie für alle Programmierer von Interesse sein – sowohl Neueinsteiger als auch Profis, die mit der MQL-Umgebung arbeiten. Darüber hinaus wäre es sinnvoll, wenn dieser Artikel von den Entwicklern und Planern der MQL-Umgebung gelesen werden würde, weil hierin nämlich einige Fragen thematisiert werden, die vielleicht in die künftigen Implementierungen von MetaTrader und MQL miteinbezogen werden könnten.
MQL4 Sprache für Einsteiger. Einführung MQL4 Sprache für Einsteiger. Einführung
Diese Artikelreihe ist für Trader gedacht, die nichts über Programmierung wissen, aber den Wunsch haben MQL4 so schnell wie möglich mit minimal aufgewendeter Zeit und Anstrengung zu lernen. Wenn Sie Angst vor solchen Begriffen wie "Objektorientierung" oder "dreidimensionale Arrays" haben, ist dieser Artikel das was Sie brauchen. Die Lektionen sind für ein maximal schnelles Ergebnis entworfen. Darüber hinaus werden die Informationen in verständlicher Form geliefert. Wie werden nicht zu tief in die Theorie gehen, aber Sie werden den praktischen Nutzen bereits ab der ersten Lektion gewinnen.
Der Indikator Taichi - Eine simple Methode zur Formalisierung der Werte von Ichimoku Kinko Hyo. Der Indikator Taichi - Eine simple Methode zur Formalisierung der Werte von Ichimoku Kinko Hyo.
Finden Sie, dass es schwierig ist, Ichimoku-Signale zu interpretieren? Dieser Artikel stellt einige Prinzipien vor, mit deren Hilfe man bestimmte Werte und Signal von Ichimoku Kinko Hyo standardisieren kann. Zur Visualisierung ihrer Benutzung hat sich der Autor für das Währungspaar EURUSD entschieden, was allein seinen eigenen Präferenzen geschuldet ist. Natürlich kann dieser Indikator in Verbindung mit jedem beliebigen Währungspaar eingesetzt werden.
Strings: Tabelle mit ASCII Zeichen und ihre Verwendung Strings: Tabelle mit ASCII Zeichen und ihre Verwendung
In diesem Artikel analysieren wir die Tabelle mit ASCII Zeichen und die Wege, in denen sie verwendet werden können. Wir werden uns außerdem mit einigen neuen Funktionen, dem Prinzip der Arbeitsweise, die auf den Besonderheiten der ASCII Tabelle basiert, beschäftigen, und dann werden wir eine neue Bibliothek (Libary) erstellen, die diese Funktionen enthält. Sie sind in anderen Programmiersprachen ziemlich beliebt, aber sie sind nicht in der Liste eingebauter Funktionen enthalten. Außerdem werden wir die Grundlagen der Arbeit mit Strings im Detail untersuchen. Also, ich glaube, Sie werden sicherlich etwas Neues über diesen nützlichen Datentyp erfahren.