OnTesterInit

전략 테스터에서 최적화하기 전에 필요한 작업을 수행하기 위해 TesterInit 이벤트가 발생하면 EA에서 이 기능을 호출합니다. 두 가지 함수 유형이 있습니다.

결과를 반환하는 버전

int  OnTesterInit(void);

반환 값

int 타입 값, 0은 최적화가 시작되기 전에 차트에서 EA가 성공적으로 초기화되었음을 의미합니다.

실행 결과를 반환하는 OnTesterInit() 호출은 프로그램 초기화를 허용할 뿐만 아니라 초기 최적화 중지 시 오류 코드를 반환하므로 사용을 권장합니다. INIT_SUCCEEDED (0) 이외의 값이 반환되면 오류가 발생하며 최적화가 시작되지 않습니다.

결과 반환 없는 버전은 이전 코드와의 호환성을 위해서만 유지됩니다. 이용을 권장하지 않습니다

void  OnTesterInit(void);

참고

TesterInit 이벤트는 전략 테스터에서 EA 최적화가 시작되기 전에 생성됩니다. 이 이벤트에서는 OnTesterDeInit() 또는 OnTesterPass() 이벤트 핸들러가 있는 EA가 별도의 터미널 차트에 자동으로 다운로드됩니다. 여기에는 테스터에 지정된 심볼과 기간이 있습니다.

그러한 이벤트는 TesterInit, TesterDeinit 및 TesterPass 이벤트를 수신하며, Init, Deinit 및 NewTick은 수신하지 않습니다. 따라서 최적화 중 각 패스의 결과를 처리하는 데 필요한 모든 논리는 OnTesterInit (), OnTesterDeinit () 및 OnTesterPass () 핸들러에 구현되어야 합니다.

전략 최적화 중 단일 패스의 결과는 OnTester () 핸들러의 프레임을 통해 FrameAdd () 함수를 사용하여 전달될 수 있습니다.

OnTesterInit() 기능은 최적화를 시작하기 전에 Expert Advisor를 시작하여 추가적인 최적화 결과 처리를 수행하는 데 사용됩니다. 이것은 항상 OnTesterDeinit() 핸들러와 함께 사용됩니다.

OnTesterInit() 실행 시간은 제한되어 있습니다. 이 값을 초과하면 EA가 강제로 중지되고 최적화 자체는 취소됩니다. 테스터 저널에 다음과 같은 메시지가 표시됩니다:

Tester        OnTesterInit가 너무 오래 작업하고 있습니다. 테스터가 초기화될 수 없습니다.

이 예제는 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() 핸들러 포함 샘플 EA,"
#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;  // EA's MagicNumber
//--- ATR 지표 핸들 보관용
int atr_handle;
//--- 여기에 마지막 ATR 값과 캔들 바디를 저장할 것입니다.
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//--- 최적화 시작 시간 기억
datetime optimization_start;
//--- 최적화 종료 후 차트에 지속 시간 표시
string report;
//+------------------------------------------------------------------+
//| TesterInit 함수                                              |
//+------------------------------------------------------------------+
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: optimization launched at %s",
                       __FUNCTION__,TimeToString(TimeLocal(),TIME_MINUTES|TIME_SECONDS));
//--- 차트 및 터미널 저널에 메시지 표시
   Print(report);
   Comment(report);
//---   
  }
//+------------------------------------------------------------------+
//| TesterDeinit 함수                                            |
//+------------------------------------------------------------------+
void OnTesterDeinit()
  {
//--- 최적화 기간
   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 초기화 함수                                   |
//+------------------------------------------------------------------+
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);
     }
//--- EA 초기화 성공
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert 틱 함수                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- 거래 시그널
   static int signal=0; // +1은 매수 시그널을, -1은 매도 시그널을 의미합니다
//--- 'holdbars' 막대 보다 이전에 오픈된 포지션을 확인 후 마감합니다
   ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- 새 막대 점검
   if(isNewBar())
     {
      //--- 시그널 유무를 확인      
      signal=CheckSignal();
     }
//--- netting 포지션이 오픈한 경우 시그널을 스킵합니다 - 시그널 마감까지 대기하십시오
   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: 최근 막대 수령에 실패! Error",__FUNCTION__,GetLastError());
     }
   else
      PrintFormat("%s: ATR 지표 값 수신 실패! Error",__FUNCTION__,GetLastError());
//--- 역거래 모드가 활성화된 경우
   res=revers?-res:res;  // 필요한 경우 시그널을 역방향으로(1이 아닌 -1을 반환하고 그 반대로도 반환)
//--- 거래 시그널 값을 반환
   return (res);
  }
//+------------------------------------------------------------------+
//|  새 막대가 나타나면 'true'를 반환                            |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
  {
   static datetime bartime=0; // 현재 막대의 오픈 시간 저장
//--- 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)
  {
//--- 구조 선언 및 초기화
   MqlTradeRequest request={0};
   MqlTradeResult  result={0};
   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);
  }

더 보기

Testing trading strategies, Working with optimization results, OnTesterDeinit, OnTesterPass, ParameterGetRange, ParameterSetRange