시스템트레이딩 게시글

MetaTrader 4에서 ForexFactory.com의 고강도 이벤트를 가져오는 방법

첨부파일
15505.zip (2.86 KB, 다운로드 2회)

안녕하세요, 트레이더 여러분! 요즘 원유와 브렌트에 대한 EA(Expert Advisor)를 개발하고 있는데요, 이 과정에서 ForexFactory.com에서 '원유 재고' 보고서의 정확한 날짜와 시간을 가져오는 방법을 공유하려고 합니다. 이 보고서는 보통 수요일 오전 10시 30분에 발표되지만, 휴일이 있으면 발표일이 달라질 수 있습니다. 제 EA에 중요한 보고서이기 때문에, 정확한 발표일을 확인하기 위해 온라인 서비스를 체크할 수밖에 없었습니다.

1. 웹사이트 설정하기

먼저, OPTIONS | EXPERT ADVISOR 탭에서 WebRequest를 사용할 웹사이트를 추가해야 합니다. 사용할 주소는 'http://www.forexfactory.com/'입니다. 아래 이미지를 참고하세요.

2. 이벤트 구조 정의하기

다음으로, 코드 상단에 이벤트를 저장할 구조체를 정의해야 합니다. 여기서 'DailyEvents'를 전역 변수로 선언하고, 저장할 최대 이벤트 수를 'MaxDailyEvents' 변수로 정합니다.

// 이벤트 구조체 정의
struct EVENTS
{
   string   time;
   string   title;
   string   currency;
   bool     displayed;
};

#define     MaxDailyEvents       20    // 20개 이상의 고강도 이벤트가 있을 것으로 예상되면 이 숫자를 늘리세요.
EVENTS      DailyEvents[MaxDailyEvents];

3. HTML 코드 가져오기 및 파싱하기

이제 ForexFactory.com에서 HTML 코드를 가져와야 합니다. HTML 코드에 익숙하지 않더라도 걱정하지 마세요, 제가 차근차근 설명해 드릴게요 :)

먼저, WebRequest를 위한 URL을 구성해야 합니다. 오늘 날짜의 캘린더만 필요하므로 'day' 파라미터를 오늘 날짜로 설정하고 요청을 보냅니다.

string   url="http://www.forexfactory.com/calendar.php?day="; url += MthName(Month()) + DoubleToStr(Day(), 0) + "." + DoubleToStr(Year(), 0);

그 다음 요청을 보내고, 오류 코드를 확인합니다(만약 있다면). 반환된 문자 배열을 문자열로 변환하여 HTML 코드를 더 쉽게 파싱할 수 있습니다.

// 웹 요청 보내기
   ResetLastError();
   res = WebRequest("GET", url, cookie, NULL, timeout, post, 0, result, headers);

   // 오류 확인
   if(res == -1)
   {
      Print("웹 요청에서 오류 발생. 오류 코드 = ", GetLastError());
      MessageBox("'http://forexfactory.com/' 주소를 허용된 URL 목록에 추가하세요."
, "오류", MB_ICONINFORMATION);
      return(false);
   }

4. 이벤트 데이터 파싱하기

오류가 없다면, 문자 배열 'result'를 문자열로 변환해 파싱을 시작합니다. HTML에서 오늘 날짜에 해당하는 데이터를 찾고, 필요한 HTML 태그의 값을 가져오는 'GetHTMLElement' 함수를 사용합니다.

// 캘린더가 오늘 날짜인지 확인
int i = StringFind(HTML, "<span class=\"date\">");
if(i == -1) return(false);
HTML = StringSubstr(HTML, i);
string date = GetHTMLElement(HTML, "<span>", "</span>");
if(date != MthName(Month()) + " " + DoubleToStr(Day(), 0)) return(false);

5. 이벤트 출력하기

이제 모든 테이블 행을 파싱하고, 필요한 이벤트 정보를 'DailyEvents' 구조체에 추가합니다. 마지막으로 차트에 이벤트를 표시합니다. 만약 이벤트가 미래에 있다면, 수직선을 표시하고, 과거라면 표시하지 않습니다.

// 고강도 이벤트 표시하기
lasttime = NULL;
for(cntr = 0; cntr < MaxDailyEvents; cntr++)
{
   if(StringLen(DailyEvents[cntr].time) == 0) break;
      
   // 차트에 이벤트 마커 생성하기
   if(lasttime != DailyEvents[cntr].time)
   {
      res = cntr;
      // 문자열에 'pm'이 있으면, 시간에 12시간 추가
      if(StringFind(DailyEvents[cntr].time, "pm") != -1) DailyEvents[cntr].time = TimeToStr(StrToTime(DailyEvents[cntr].time) + 43200);
      if(ObjectCreate(0, Event + cntr, OBJ_EVENT, 0, StrToTime(DailyEvents[cntr].time), 0))
      {
         ObjectSetString(0, Event + cntr, OBJPROP_TEXT, DailyEvents[cntr].title + " (" + DailyEvents[cntr].currency + ")");
         ObjectSetInteger(0, Event + cntr, OBJPROP_COLOR, Red);
         ObjectSetInteger(0, Event + cntr, OBJPROP_WIDTH, 2);
         ObjectSetInteger(0, Event + cntr, OBJPROP_BACK, true);
         ObjectSetInteger(0, Event + cntr, OBJPROP_SELECTABLE, false);
         ObjectSetInteger(0, Event + cntr, OBJPROP_SELECTED, false);
         ObjectSetInteger(0, Event + cntr, OBJPROP_HIDDEN, true);
         ObjectSetString(0, Event + cntr, OBJPROP_TOOLTIP, DailyEvents[cntr].title + " (" + DailyEvents[cntr].currency + ")");
      }
      
      // 이벤트가 미래라면 수직선 생성
      if(TimeCurrent() < TimeOffset(DailyEvents[cntr].time, 0))
      {
         if(ObjectCreate(0, VLine + cntr, OBJ_VLINE, 0, TimeOffset(DailyEvents[cntr].time, 0), 0))
         {
            ObjectSetInteger(0, VLine + cntr, OBJPROP_COLOR, Red);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_WIDTH, 1);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_BACK, true);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_SELECTABLE, false);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_SELECTED, false);
            ObjectSetInteger(0, VLine + cntr, OBJPROP_HIDDEN, true);
            ObjectSetString(0, VLine + cntr, OBJPROP_TOOLTIP, DailyEvents[cntr].title + " (" + DailyEvents[cntr].currency + ")");
         }
      }
      else
         DailyEvents[cntr].displayed = true;
   }
   else
   {
      title = ObjectGetString(0, Event + res, OBJPROP_TOOLTIP);
      title += "\n" + DailyEvents[cntr].title + " (" + DailyEvents[cntr].currency + ")";
      ObjectSetString(0, Event + res, OBJPROP_TOOLTIP, title);
      if(TimeCurrent() < TimeOffset(DailyEvents[cntr].time, 0)) ObjectSetString(0, VLine + res, OBJPROP_TOOLTIP, title);
   }
   lasttime = DailyEvents[cntr].time;
}

6. 알림 설정하기

미래의 이벤트가 있을 경우, 현재 시간으로부터 5분 이내에 다가오는 이벤트에 대해 사용자에게 알림을 보내고 수직선을 제거하는 코드를 'start()' 함수에 추가해야 합니다.

//+------------------------------------------------------------------+
//| EA 시작 함수 |
//+------------------------------------------------------------------+
void start()
{
   string   event = NULL;
   
   // 5분 이내에 고강도 이벤트가 있는지 확인
   for(int i = 0; i < MaxDailyEvents; i++)
   {
      if(StringLen(DailyEvents[i].time) == 0) break;
      if(TimeCurrent() >= StrToTime(DailyEvents[i].time) - 300 && TimeCurrent() < StrToTime(DailyEvents[i].time) && !DailyEvents[i].displayed)
      {
         // 이벤트가 5분 이내에...
         event += DailyEvents[i].title + " (" + DailyEvents[i].currency + "), ";
         DailyEvents[i].displayed = true;
         
         // 이벤트와 관련된 수직선 삭제
         if(ObjectFind("VLine" + DoubleToStr(i, 0)) >= 0) ObjectDelete("VLine" + DoubleToStr(i, 0));
      }
   }
   
   // 표시할 내용이 있는지 확인
   if(StringLen(event) != 0)
   {
      event += "에서 5분 내로.";
      Alert(event);
   }
}

7. 초기화 함수 추가하기

마지막으로, 매일 이벤트를 가져오는 코드를 OnInit() 함수에 추가해야 합니다.

//+------------------------------------------------------------------+
//| EA 초기화 함수 |
//+------------------------------------------------------------------+
int OnInit()
{
   // 오늘의 이벤트 가져오기
   GetHighImpactEvents();
   
   return(INIT_SUCCEEDED);
}

이렇게 간단하게 설정할 수 있습니다. 물론 이 코드를 수정하여 모든 이벤트를 표시하거나, 표시할 영향을 지정하는 입력 파라미터를 추가할 수도 있습니다. 자정을 체크하여 새로운 일일 이벤트 목록을 가져오는 체크도 추가할 수 있는데, 그 부분은 여러분이 직접 실험해보세요 :)

감사합니다!

- 클로드.

연관 포스트

댓글 (0)