OnTesterInit

Вызывается в экспертах при наступлении события TesterInit для выполнения необходимых действий перед началом оптимизации в тестере стратегий. Существуют два варианта функции.

Версия с возвратом результата

int  OnTesterInit(void);

Возвращаемое значение

Значение типа int, ноль означает успешную инициализацию эксперта, запущенного на графике перед началом оптимизации.

Приоритетным является использование вызова OnTesterInit() с возвратом результата выполнения, так как этот способ позволяет не только выполнить инициализацию программы, но и вернуть код ошибки в случае досрочного прекращения оптимизации. Возврат любого значения, отличного от INIT_SUCCEEDED (0), означает ошибку и оптимизация запущена не будет.

Версия без возврата результата оставлена только для совместимости со старыми кодами. Не рекомендуется к использованию

void  OnTesterInit(void);

Примечание

Событие TesterInit автоматически генерируется перед началом оптимизации эксперта в тестере стратегий. По данному событию эксперт, имеющий обработчик OnTesterDeinit() и/или OnTesterPass(), автоматически загружается на отдельном графике терминала с указанными в тестере символом и периодом.

Такой эксперт получает события TesterInit, TesterDeinit и TesterPass, но не получает событий Init, Deinit и NewTick. Соответственно, всю необходимую логику по обработке результатов каждого прохода в процессе оптимизации необходимо реализовать в обработчиках OnTesterInit(), OnTesterDeinit() и OnTesterPass().

Результат каждого одиночного прохода при оптимизации стратегии можно передать через фрейм из обработчика OnTester() с помощью функции FrameAdd().

Функция OnTesterInit() предназначена для инициализации эксперта перед началом оптимизации для последующей обработки результатов оптимизации. Всегда используется совместно с обработчиком OnTesterDeinit().

На выполнение OnTesterInit() отводится ограниченное время, по превышении которого будет произведено принудительное завершение работы эксперта, а сама оптимизация будет отменена. При этом в Журнал тестера будет выведено сообщение:

Tester        OnTesterInit works too long. Tester cannot be initialized.

Пример взят из OnTick, добавлен обработчик OnTesterInit() для установки параметров оптимизации:

//+------------------------------------------------------------------+
//|                                          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 "Пример советника с обработчиком OnTesterInit()"
#property description "в котором устанавливаются зачения и границы "
#property description "входных параметров при оптимизации"
 
input double lots=0. 1;       // объем в лотах
input double kATR=3;          // длина сигнальной свечи в ATR
input int    ATRperiod=20;    // период индикатора ATR
input int    holdbars=8;      // сколько баров удерживаем позицию
input int    slippage=10;     // допустимое проскальзывание
input bool   revers=false;    // переворачиваем сигнал? 
input ulong  EXPERT_MAGIC=0;  // MagicNumber эксперта
//--- для хранения хендла индикатора ATR
int atr_handle;
//--- здесь будем хранить последние значения ATR и тела свечи
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//--- запоминаем время начала оптимизации
datetime optimization_start;
//--- для вывода на график длительности после окончании оптимизации 
string report;
//+------------------------------------------------------------------+
//| TesterInit function                                              |
//+------------------------------------------------------------------+
void OnTesterInit()
  {
//--- установим значения входных параметров для оптимизации
   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("Установлены начальные значения и границы параметров оптимизации");
//--- запомним начало оптимизации
   optimization_start=TimeLocal();
   report=StringFormat("%s: оптимизация запущена в %s",
                       __FUNCTION__,TimeToString(TimeLocal(),TIME_MINUTES|TIME_SECONDS));
//--- выведем собщения на график и в журнал терминала
   Print(report);
   Comment(report);
//---   
  }
//+------------------------------------------------------------------+
//| TesterDeinit function                                            |
//+------------------------------------------------------------------+
void OnTesterDeinit()
  {
//--- продолжительность оптимизации
   string log_message=StringFormat("%s: оптимизация заняла %d секунды",
                                   __FUNCTION__,TimeLocal()-optimization_start);
   PrintFormat(log_message);
   report=report+"\r\n"+log_message;
   Comment(report);
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- инициализируем глобальные переменные
   last_atr=0;
   last_body=0;
//--- установим правильный объем
   double min_lot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   trade_lot=lots>min_lot? lots:min_lot;   
//--- создадим хендл индикатора ATR
   atr_handle=iATR(_Symbol,_Period,ATRperiod);
   if(atr_handle==INVALID_HANDLE)
     {
      PrintFormat("%s: не удалось создать iATR, код ошибки %d",__FUNCTION__,GetLastError());
      return(INIT_FAILED);
     }
//--- успешная инициализация эксперта
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- торговый сигнал
   static int signal=0; // +1 означает сигнал на покупку, -1 означает сигнал на продажу
//--- проверим и закроем старые позиции, открытые более holdbars баров назад 
   ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- проверим появление нового бара
   if(isNewBar())
     {
      //--- проверим наличие сигнала      
      signal=CheckSignal();
     }
//--- если открыта неттинговая позиция, то сигнал пропускаем - ждем, пока она закроется
   if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
     {
      signal=0;
      return// выходим из обработчика события NewTick и не входим в рынок до появления нового бара
     }
//--- для хеджингового счета каждая позиция живет и закрывается раздельно
   if(signal!=0)
     {
      //--- сигнал на покупку
      if(signal>0)
        {
         PrintFormat("%s: Есть сигнал на покупку! Revers=%s",__FUNCTION__,string(revers));
         if(Buy(trade_lot,slippage,EXPERT_MAGIC))
            signal=0;
        }
      //--- сигнал на продажу
      if(signal<0)
        {
         PrintFormat("%s: Есть сигнал на продажу! Revers=%s",__FUNCTION__,string(revers));
         if(Sell(trade_lot,slippage,EXPERT_MAGIC))
            signal=0;
        }
     }
//--- конец функции OnTick
  }
//+------------------------------------------------------------------+
//| Проверяет наличие торгового сигнала                              |
//+------------------------------------------------------------------+
int CheckSignal()
  {
//--- 0 означает отсутствие сигнала
   int res=0;
//--- получим значение ATR на предпоследнем завершенном баре (индекс бара равен 2)
   double atr_value[1];
   if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1)
     {
      last_atr=atr_value[0];
      //--- получим данные последнего закрытого бара в массив типа MqlRates
      MqlRates bar[1];
      if(CopyRates(_Symbol,_Period,1,1,bar)!=-1)
        {
         //--- вычислим размер тела бара на последнем закрытом баре
         last_body=bar[0].close-bar[0].open;
         //--- если тело последнего бара (с индексом 1) превышает предыдущее значение ATR (на баре с индексом 2), то торговый сигнал получен
         if(MathAbs(last_body)>kATR*last_atr)
            res=last_body>0?1:-1; // для растущей свечи положительное значение
        }
      else
         PrintFormat("%s: Не удалось получить последний бар! Ошибка",__FUNCTION__,GetLastError());
     }
   else
      PrintFormat("%s: Не удалось получить значение индикатора ATR! Ошибка",__FUNCTION__,GetLastError());
//--- если включен реверсивный режим торговли
   res=revers?-res:res;  // если нужно, то развернем сигнал (вместо 1 вернем -1, а вместо -1 вернем +1)
//--- вернем значение торгового сигнала
   return (res);
  }
//+------------------------------------------------------------------+
//|  Возвращает true при появлении нового бара                       |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
  {
   static datetime bartime=0; // храним время открытия текущего бара
//--- получим время открытия нулевого бара
   datetime currbar_time=iTime(_Symbol,_Period,0);
//--- если время открытия изменилось, значит появился новый бар
   if(bartime!=currbar_time)
     {
      bartime=currbar_time;
      lastbar_timeopen=bartime;
      //--- нужно ли выводить в лог информацию о времени открытия нового бара      
      if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER)))
        {
         //--- выведем сообщение о времени открытия нового бара
         PrintFormat("%s: new bar on %s %s opened at %s",__FUNCTION__,_Symbol,
                     StringSubstr(EnumToString(_Period),7),
                     TimeToString(TimeCurrent(),TIME_SECONDS));
         //--- получим данные о последнем тике
         MqlTick last_tick;
         if(!SymbolInfoTick(Symbol(),last_tick))
            Print("SymbolInfoTick() failed, error = ",GetLastError());
         //--- выведем время последнего тика с точностью до миллисекунд
         PrintFormat("Last tick was at %s.%03d",
                     TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000);
        }
      //--- у нас есть новый бар
      return (true);
     }
//--- нового бара нет
   return (false);
  }
//+------------------------------------------------------------------+
//| Покупка по рынку с заданным объемом                              |
//+------------------------------------------------------------------+
bool Buy(double volume,ulong deviation=10,ulong  magicnumber=0)
  {
//--- покупаем по рыночной цене
   return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber));
  }
//+------------------------------------------------------------------+
//| Продажа по рынку с заданным объемом                              |
//+------------------------------------------------------------------+
bool Sell(double volume,ulong deviation=10,ulong  magicnumber=0)
  {
//--- продаем по рыночной цене
   return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber));
  }
//+------------------------------------------------------------------+
//| Закрытие позиций по времени удержания в барах                    |
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong  magicnumber=0)
  {
   int total=PositionsTotal(); // количество открытых позиций   
//--- перебор всех открытых позиций
   for(int i=total-1; i>=0; i--)
     {
      //--- параметры позиции
      ulong  position_ticket=PositionGetTicket(i);                                      // тикет позиции
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // символ 
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber позиции
      datetime position_open=(datetime)PositionGetInteger(POSITION_TIME);               // время открытия позиции
      int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1;                       // сколько баров назад была открыта позиция
 
      //--- если позиция живет уже долго, а также MagicNumber и символ совпадают
      if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol)
        {
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);           // количество знаков после запятой
         double volume=PositionGetDouble(POSITION_VOLUME);                              // объем позиции
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // тип позиции
         string str_type=StringSubstr(EnumToString(type),14);
         StringToLower(str_type); // понижаем регистр текста для правильного форматирования сообщения
         PrintFormat("Закрываем позицию #%I64u %s %s %.2f",
                     position_ticket,position_symbol,str_type,volume);
         //--- установка типа ордера и отправки торгового запроса
         if(type==POSITION_TYPE_BUY)
            MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket);
         else
            MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket);
        }
     }
  }
//+------------------------------------------------------------------+
//| Подготовка и отправка торгового запроса                          |
//+------------------------------------------------------------------+
bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0)
  {
//--- объявление и инициализация cтруктур
   MqlTradeRequest request={};
   MqlTradeResult  result={};
   double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
   if(type==ORDER_TYPE_BUY)
      price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
//--- параметры запроса
   request.action   =TRADE_ACTION_DEAL;                     // тип торговой операции
   request.position =pos_ticket;                            // тикет позиции, если закрываем
   request.symbol   =Symbol();                              // символ
   request.volume   =volume;                                // объем 
   request.type     =type;                                  // тип ордера
   request.price    =price;                                 // цена совершения сделки
   request.deviation=slip;                                  // допустимое отклонение от цены
   request.magic    =magicnumber;                           // MagicNumber ордера
//--- отправка запроса
   if(!OrderSend(request,result))
     {
      //--- выведем информацию о неудаче
      PrintFormat("OrderSend %s %s %.2f at %.5f error %d",
                  request.symbol,EnumToString(type),volume,request.price,GetLastError());
      return (false);
     }
//--- сообщим об успешной операции
   PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
   return (true);
  }

Смотри также

Тестирование торговых стратегий, Работа с результатами оптимизации, OnTesterDeinit, OnTesterPass, ParameterGetRange, ParameterSetRange