OnTesterInit

Die Funktion wird von EAs aufgerufen, wenn das Ereignis TesterInit auftritt, um notwendige Aktionen vor der Optimierung im Strategie-Tester durchzuführen. Es gibt zwei Versionen dieser Funktion.

Die Version, die das Ergebnis zurück gibt

int  OnTesterInit(void);

Rückgabewert

Vom Typ int, Null bedeutet die erfolgreiche Initialisierung eines auf einem Chart gestarteten EAs vor dem Start der Optimierung.

Der Aufruf OnTesterInit(), der das Ausführungsergebnis zurückgibt, wird zur Verwendung empfohlen, da er nicht nur die Programminitialisierung erlaubt, sondern auch einen Fehlercode im Falle eines frühen Optimierungsstopps zurückgibt. Die Rückgabe eines anderen Wertes als INIT_SUCCEEDED (0) bedeutet einen Fehler, es wird keine Optimierung gestartet.

Die Version ohne Ergebnisrückgabe wird nur aus Kompatibilitätsgründen mit alten Codes belassen. Ein Verwenden wird nicht empfohlen.

void  OnTesterInit(void);

Hinweis

Das Ereignis TesterInit wird generiert, bevor die EA-Optimierung im Strategie-Tester startet. Bei diesem Ereignis wird ein EA mit OnTesterDeInit() oder OnTesterPass() Ereignisbehandler automatisch auf ein separates Terminalchart geladen. Es hat das Symbol und den Zeitrahmen, die im Tester angegeben wurden.

So ein Ereignis erhält die Ereignisse von TesterInit, TesterDeinit und TesterPass, aber nicht Init, Deinit und NewTick. Dementsprechend sollte die notwendige Logik für die Verarbeitung der Ergebnisse jedes Durchlaufs während der Optimierung in den Funktionen OnTesterInit(), OnTesterDeinit() und OnTesterPass() vollständig implementiert werden.

Das Ergebnis jedes einzelnen Durchlaufs während einer Strategieoptimierung kann über einen Frame der Funktion OnTester() mit der Funktion FrameAdd() übergeben werden.

Die Funktion OnTesterInit() wird verwendet, um vor dem Start der Optimierung einen Expert Advisor für weitere zu initiieren, der die Optimierungsergebnisse verarbeitet. Sie wird immer zusammen mit OnTesterDeinit() verwendet.

Die Zeit für die Ausführung von OnTesterInit() ist begrenzt. Wird sie überschritten, wird der EA zwangsweise gestoppt und die Optimierung abgebrochen. Im Tester-Journal wird eine Meldung angezeigt:

Tester        OnTesterInit dauert zu lange. Der Tester kann nicht initialisiert werden.

Das Beispiel wird von OnTick übernommen. Die Funktion OnTesterInit() wurde hinzugefügt, um Optimierungsparameter : zu setzen.

//+------------------------------------------------------------------+
//|                                          OnTesterInit_Sample.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "Sample EA with the OnTesterInit() handler,"
#property description "in which values and limitations of "
#property description "inputs during optimization are set"
 
input double lots=0. 1;       // Volumen in Lots
input double kATR=3;          // Länge der Signalkerze in ATR
input int    ATRperiod=20;    // Periodenlänge des ATR
input int    holdbars=8;      // Anzahl der Bars, die die Position gehalten werden soll
input int    slippage=10;     // Erlaubter Schlupf
input bool   revers=false;    // Signal umkehren? 
input ulong  EXPERT_MAGIC=0;  // Des EA's Magicnummer
//--- zum Sichern des Handles des Indikators ATR
int atr_handle;
//--- hier werden die letzten Werte des ATR und die Kerzenkörper gesichert
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//--- Erinnern der Startzeit der Optimierung
datetime optimization_start;
//--- für die Anzeige der Dauer auf dem Chart nach dem Ende der Optimierung
string report;
//+------------------------------------------------------------------+
//| TesterInit Funktion                                              |
//+------------------------------------------------------------------+
void OnTesterInit()
  {
//--- Setzen der Eingabewerte für die Optimierung
   ParameterSetRange("lots",false,0.1,0,0,0);
   ParameterSetRange("kATR",true,3.0,1.0,0.3,7.0);
   ParameterSetRange("ATRperiod",true,10,15,1,30);
   ParameterSetRange("holdbars",true,5,3,1,15);
   ParameterSetRange("slippage",false,10,0,0,0);
   ParameterSetRange("revers",true,false,false,1,true);
   ParameterSetRange("EXPERT_MAGIC",false,123456,0,0,0);
   Print("Initial values and optimization parameter limitations are set");
//--- Sichern des Beginns der Optimierung
   optimization_start=TimeLocal();
   report=StringFormat("%s: optimization launched at %s",
                       __FUNCTION__,TimeToString(TimeLocal(),TIME_MINUTES|TIME_SECONDS));
//--- Anzeige der Nachrichten auf dem Chart und im Journal des Terminal
   Print(report);
   Comment(report);
//---   
  }
//+------------------------------------------------------------------+
//| TesterDeinit Funktion                                            |
//+------------------------------------------------------------------+
void OnTesterDeinit()
  {
//--- Dauer der Optimierung
   string log_message=StringFormat("%s: optimization took %d seconds",
                                   __FUNCTION__,TimeLocal()-optimization_start);
   PrintFormat(log_message);
   report=report+"\r\n"+log_message;
   Comment(report);
  }
//+------------------------------------------------------------------+
//| Expert Initialisierungsfunktion                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Initialisierung der Globalen Variablen
   last_atr=0;
   last_body=0;
//--- Setzen des korrekten Volumens
   double min_lot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   trade_lot=lots>min_lot? lots:min_lot;   
//--- Erstellen des Handles des Indikators ATR
   atr_handle=iATR(_Symbol,_Period,ATRperiod);
   if(atr_handle==INVALID_HANDLE)
     {
      PrintFormat("%s: failed to create iATR, error code %d",__FUNCTION__,GetLastError());
      return(INIT_FAILED);
     }
//--- Erfolgreiche Initialisierung des EA
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Tick-Funktion des Experten                                       |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Handelssignal
   static int signal=0; // +1 heißt Kaufsignal, -1 Verkaufssignal
//--- Prüfen und Schließen alter Positionen, die vor mehr als 'holdbars' eröffnet wurden
   ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- Prüfen auf eine neue Bar
   if(isNewBar())
     {
      //--- Prüfen auf ein Signal
      signal=CheckSignal();
     }
//--- Wenn eine Netting-Position eröffnet wurde - warten bis sie geschlossen wurde
   if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
     {
      signal=0;
      return// die Ereignisbehandlung von NewTick beenden und kein Markteintritt vor dem nächsten Tick
     }
//--- für ein Hedging-Konto wird jede Position separat gehalten und geschlossen
   if(signal!=0)
     {
      //--- Kaufsignal
      if(signal>0)
        {
         PrintFormat("%s: Buy signal! Revers=%s",__FUNCTION__,string(revers));
         if(Buy(trade_lot,slippage,EXPERT_MAGIC))
            signal=0;
        }
      //--- Verkaufssignal
      if(signal<0)
        {
         PrintFormat("%s: Sell signal! Revers=%s",__FUNCTION__,string(revers));
         if(Sell(trade_lot,slippage,EXPERT_MAGIC))
            signal=0;
        }
     }
//--- Ende der Funktion OnTick
  }
//+------------------------------------------------------------------+
//| Prüfen auf ein neues Handelssignal                               |
//+------------------------------------------------------------------+
int CheckSignal()
  {
//--- 0 beutet klein Signal
   int res=0;
//--- Abfrage des Wertes der ATR der vorletzten kompletten Bar (Indes der Bar ist 2)
   double atr_value[1];
   if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1)
     {
      last_atr=atr_value[0];
      //--- Datenabfrage der letzten geschlossenen Bar von Array des Typs MqlRates
      MqlRates bar[1];
      if(CopyRates(_Symbol,_Period,1,1,bar)!=-1)
        {
         //--- Berechnen der Körpergröße der letzten, vollständigen Kerze
         last_body=bar[0].close-bar[0].open;
         //--- wenn der Körper der letzten Bar (mit Index 1) den vorherigen ATR-Wert überschreitet (auf der Bar mit Index 2), wird ein Handelssignal empfangen.
         if(MathAbs(last_body)>kATR*last_atr)
            res=last_body>0?1:-1; // positiver Wert der Aufwärtskerze
        }
      else
         PrintFormat("%s: Failed to receive the last bar! Error",__FUNCTION__,GetLastError());
     }
   else
      PrintFormat("%s: Failed to receive ATR indicator value! Error",__FUNCTION__,GetLastError());
//--- falls der umgekehrte Handelsmodus aktiviert ist
   res=revers?-res:res;  // Signal umkehren, wenn nötig (Rückgabe von -1 statt 1 und vice versa)
//--- Rückgabe des Wertes des Handelssignals
   return (res);
  }
//+------------------------------------------------------------------+
//|  Rückgabe von 'true' wenn eine neue Bar erscheint                |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
  {
   static datetime bartime=0; // Sichern der Eröffnungszeit der aktuellen Bar
//--- Abfrage der Eröffnungszeit der Bar Null
   datetime currbar_time=iTime(_Symbol,_Period,0);
//--- Wenn sich die Eröffnungszeit änderte, gibt es eine neue Bar
   if(bartime!=currbar_time)
     {
      bartime=currbar_time;
      lastbar_timeopen=bartime;
      //--- Eintragen der Eröffnungszeit der neuen Bar in das Log
      if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER)))
        {
         //--- Anzeige der Nachricht mit der Eröffnungszeit der neuen bar
         PrintFormat("%s: new bar on %s %s opened at %s",__FUNCTION__,_Symbol,
                     StringSubstr(EnumToString(_Period),7),
                     TimeToString(TimeCurrent(),TIME_SECONDS));
         //--- Datenabfrage beim letzten Tick
         MqlTick last_tick;
         if(!SymbolInfoTick(Symbol(),last_tick))
            Print("SymbolInfoTick() failed, error = ",GetLastError());
         //--- Anzeige der Zeit des letzten Ticks bis zur Millisekunde
         PrintFormat("Last tick was at %s.%03d",
                     TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000);
        }
      //--- wir haben eine neue Bar
      return (true);
     }
//--- keine neue Bar
   return (false);
  }
//+------------------------------------------------------------------+
//| Kauf zum Marktpreis mit angegebenen Volumen                      |
//+------------------------------------------------------------------+
bool Buy(double volume,ulong deviation=10,ulong  magicnumber=0)
  {
//--- Kauf zum Marktpreis
   return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber));
  }
//+------------------------------------------------------------------+
//| Verkauf zum Marktpreis mit angegebenen Volumen                   |
//+------------------------------------------------------------------+
bool Sell(double volume,ulong deviation=10,ulong  magicnumber=0)
  {
//--- Verkauf zum Marktpreis
   return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber));
  }
//+------------------------------------------------------------------+
//| Position schließen wegen der Haltezeit                           |
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong  magicnumber=0)
  {
   int total=PositionsTotal(); // Anzahl der offenen Positionen
//--- Iterieren über die offenen Position
   for(int i=total-1; i>=0; i--)
     {
      //--- Parameter der Position
      ulong  position_ticket=PositionGetTicket(i);                                      // Ticketnummer der Position
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // Symbol 
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber der Position
      datetime position_open=(datetime)PositionGetInteger(POSITION_TIME);               // Eröffnungszeit der Position
      int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1;                       // wie viele Bars vorher wurde die Position eröffnet
 
      //--- wenn die Lebenszeit der Position lang genug ist, und MagicNummer und Symbol übereinstimme
      if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol)
        {
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);           // Dezimalstellen
         double volume=PositionGetDouble(POSITION_VOLUME);                              // Volumen der Position
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type
         string str_type=StringSubstr(EnumToString(type),14);
         StringToLower(str_type); // Kleinschreibung für das korrekte Nachrichtenformat
         PrintFormat("Close position #%I64u %s %s %.2f",
                     position_ticket,position_symbol,str_type,volume);
         //--- Setzen des Auftragsart und Senden der Handelsanfrage
         if(type==POSITION_TYPE_BUY)
            MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket);
         else
            MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket);
        }
     }
  }
//+------------------------------------------------------------------+
//| Vorbereiten und Senden der Handelsanfrage                        |
//+------------------------------------------------------------------+
bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0)
  {
//--- Deklarieren und Initialisieren der Strukturen
   MqlTradeRequest request={};
   MqlTradeResult  result={};
   double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
   if(type==ORDER_TYPE_BUY)
      price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
//--- Abfrage der Parameter
   request.action   =TRADE_ACTION_DEAL;                     // Typ der Handelsoperation
   request.position =pos_ticket;                            // Ticketnummer der zu schließenden Position
   request.symbol   =Symbol();                              // Symbol
   request.volume   =volume;                                // Volumen 
   request.type     =type;                                  // Auftragsart
   request.price    =price;                                 // Handelspreis
   request.deviation=slip;                                  // erlaubter Schlupf vom Preis
   request.magic    =magicnumber;                           // MagicNumber des Auftrags
//--- Senden einer Anfrage
   if(!OrderSend(request,result))
     {
      //--- Datenanzeige im Fehlerfall
      PrintFormat("OrderSend %s %s %.2f at %.5f error %d",
                  request.symbol,EnumToString(type),volume,request.price,GetLastError());
      return (false);
     }
//--- Information über eine erfolgreiche Operation
   PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
   return (true);
  }

Siehe auch

Testen von Handelsstrategien, Arbeit mit Ergebnisse der Optimierung, OnTesterDeinit, OnTesterPass, ParameterGetRange, ParameterSetRange