OnTesterInit

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

결과를 반환하는 버전

int  OnTesterInit(void);

반환 값

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

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

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

void  OnTesterInit(void);

참고

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

그러한 이벤트는 TesterInit, TesterDeinitTesterPass 이벤트를 수신하며, Init, DeinitNewTick은 수신하지 않습니다. 따라서 최적화 중 각 패스의 결과를 처리하는 데 필요한 모든 논리는 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