MetaTrader 5와 MQL5 경제 달력: 뉴스를 재현 가능한 있는 트레이딩 시스템으로 구현하는 방법
소개
현대 뉴스 트레이더의 주요 문제점은 파편화된 도구들과 체계적인 알고리즘 트레이딩 워크 플로우의 부재입니다. 매매를 하면서 인터넷 브라우저(뉴스 사이트 검색)와 거래 단말기에 동시에 주의를 기울이는 것은 상당히 어렵습니다.
뉴스 트레이더의 워크 플로우는 다음과 같습니다: 웹 브라우저에서 뉴스 달력을 빠르게 열고 이벤트 변경 사항을 확인 → 다가오는 이벤트를 재빨리 평가하고 매매할 내용과 방법을 결정 → MetaTrader 5 터미널로 이동해서 펜딩 오더를 설정하거나 터미널에서 뉴스 발표를 기다렸다가 결정. 이러한 시나리오에서 트레이더는 종종 작업 맥락을 놓치게 되어 뉴스와 관련한 반응이 지연되고 결국 손실로 이어집니다.
뉴스 트레이딩이 명확한 규칙, 반복 가능한 결과, 자동화된 테스트를 갖춘 엔지니어링처럼 작동하기를 원하시나요? 이 글의 목적은 MetaTrader 5 용 뉴스 레이어의 작동 아키텍처를 보여주는 것입니다: 단일 데이터 소스, 달력 API의 적절한 사용, 필터링 및 캐싱 메커니즘, 과거 이벤트를 테스터 용 리소스로 내보내기, 그리고 실시간과 테스터 간 자동 전환 기능을 통해 동일한 코드로 실시간 데이터와 과거 데이터 모두에서 일관된 결정적 결과를 도출할 수 있도록 합니다.
그림 1. 수동 트레이더가 가진 주요 문제는 도구의 파편화입니다.
수동 뉴스 트레이딩은 시대에 뒤떨어졌습니다.
첫째 외부 요인에 의존하는 뉴스 전략은 검증할 수 없습니다. 트레이딩 성공의 절반은 과거 데이터를 바탕으로 전략의 성과를 검증하는 데 달려 있습니다. 전략을 검증할 수 없으면 의사 결정에 주관성이 개입하게 됩니다. 그 전략은 가설로 남을 것이며 그것을 검증하는 데 몇 달 심지어 몇 년이라는 많은 시간이 걸리게 될 것입니다.
둘째 수동 거래는 확장하기 매우 어렵습니다. 수익이 불규칙적이고 불안정한 사업을 확장하려고 해 보세요. 그러한 확장은 문제를 더욱 악화시킬 뿐입니다. 명확한 구조, 체계화 및 최적화가 없다면 시스템 확장에 대해 생각하는 것은 무의미합니다.
셋째, 뉴스에 대한 반응이 지연되는 것은 수동 거래의 주요 문제점입니다. 당신이 외환 트레이더이고 비농업 고용지표(Non Farm Payrolls)를 거래한다고 상상해 보세요.. NFP 데이터는 일반적으로 한 달에 한 번 발표됩니다. 당신은 한눈팔다가 NFP를 놓쳐서 돈을 벌 기회를 놓쳤습니다. 이는 많은 트레이더들에게 익숙한 상황입니다.
결론: 자동화만이 재현 가능한 뉴스 트레이딩을 위한 유일한 길입니다.
그림 2. 로봇은 언제나 더 빠릅니다.
MetaTrader 5에 내장된 경제 달력: 하나의 정보 출처
뉴스 트레이딩을 알고리즘화 하고 과거 데이터에서 테스트를 가능하게 하려면 저희 회사의 트레이딩 플랫폼을 사용하세요 - MQL5 API를 통해 뉴스에 접근하는 MetaTrader 5에 내장된 경제 달력. 이것이 바로 뉴스 트레이딩을 즉흥적인 방식에서 검증 가능한 알고리즘 프로세스로 전환시켜 주는 것입니다. 현재 발생하고 있는 실시간 이벤트와 과거 이벤트 모두 오프라인 테스트가 가능합니다.
표 1:
MetaTrader 5에 내장된 달력이 특별한 이유는 무엇일까요:
| 매개 변수 | |
|---|---|
| 접속 속도 | <100ms — 터미널에 뉴스가 로드된 후 |
| 통합 | 네이티브 — 터미널 코어 레벨 + MQL-API |
| 테스트 | 터미널로 뉴스를 다운로드한 후 완벽하게 지원됨(테스터에서 파일, 리소스 또는 SQLite를 통해 접근 가능). |
| 신뢰성 | 높음 |
MetaTrader 5는 뛰어난 전략 테스터를 갖추고 있습니다 - 빠르고 다양한 자산을 지원하며 로컬 및 글로벌 네트워크에 있는 컴퓨터를 사용하여 테스트 및 최적화를 수행할 수 있습니다. 뉴스 트레이딩 시스템을 과거 데이터에 적용해 보세요. 이것이 여러분의 매매 방식을 더 나은 방향으로 바꿀 수도 있습니다.
MetaTrader 5 경제 달력 기능: MQL5 API 개요
수동 분석에서 알고리즘 분석으로의 전환은 데이터 아키텍처를 이해하는 것에서 부터 시작됩니다. MQL5에서 경제 달력은 단순한 테이블이 아니라 네이티브 API를 통해 접근 가능한 구조화된 데이터베이스입니다. 이벤트를 쿼리 하는 방법에 대해 자세히 살펴보고 이벤트와 값의 차이점 그리고 성공적인 전략을 위해 시간 동기화가 필수적인 이유를 알아보겠습니다.
MQL5 달력의 핵심은 각각이 고유한 작업을 수행하는 몇 가지 핵심 함수들로 구성됩니다. 모든 경제 달력 데이터를 한 번에 제공하는 보편 함수는 없습니다. 통합적인 접근 방식이 필요합니다.
- CalendarValueHistory — 초기 로딩을 하기 위한 주요 도구입니다. 지정된 시간 간격 동안의 이벤트 값의 배열을 가져올 수 있습니다. 이는 EA 초기화 과정에서 과거 데이터 또는 향후 일주일치 데이터를 캐시에 채우는 데 사용되는 "중요한 무기"와 같은 것입니다.
- CalendarValueLast — 실시간으로 작동하는 EA의 주요 함수입니다. 마지막 요청 이후 변경되었거나 새로 추가된 값만 반환합니다(change_id 메커니즘을 통해). 이를 통해 매 틱마다 전체 데이터 배열을 요청하지 않을 수 있어 트래픽과 서버 리소스를 절약할 수 있습니다.
- CalendarEventByCountry — 해당 국가에서 열리는 모든 이벤트 대한 설명을 확인합니다. ISO 3166-1 alpha-2에 따라 코드로 지정된 특정 국가의 이벤트 설명 목록을 반환합니다. 필터를 만드는 데 필요합니다(예: "US"(미국), "RU"(러시아), "CA"(캐나다) 등의 이벤트만 표시).
- CalendarEventByCurrency — 통화별 모든 이벤트 설명을 가져옵니다. 코드로 지정된 특정 통화("USD", "EUR" 등)에 대한 이벤트 설명 목록을 반환합니다.
- CalendarCountryById — 'id'로 국가 속성을 가져옵니다.
- CalendarEventById — 'id'로 이벤트 속성을 가져옵니다.
- CalendarValueById — 'id'로 특정한 값을 가져옵니다.
데이터 구조: 달력 MQL5 API는 어떤 값을 반환하나요?
모든 경제 달력 API 함수는 구조체 배열이나 단일 구조체를 포함하는 변수를 반환합니다. 이제 각 구조와 그 영역을 모두 나열해 보겠습니다.
이벤트 설명 - CalendarEventById, CalendarEventByCountry 및 CalendarEventByCurrency 함수에서 사용:
struct MqlCalendarEvent { ulong id; // event ID ENUM_CALENDAR_EVENT_TYPE type; // event type from the ENUM_CALENDAR_EVENT_TYPE enumeration ENUM_CALENDAR_EVENT_SECTOR sector; // sector an event is related to ENUM_CALENDAR_EVENT_FREQUENCY frequency; // event frequency (periodicity) ENUM_CALENDAR_EVENT_TIMEMODE time_mode; // event time mode ulong country_id; // country ID ENUM_CALENDAR_EVENT_UNIT unit; // economic indicator value's unit of measure ENUM_CALENDAR_EVENT_IMPORTANCE importance; // event importance ENUM_CALENDAR_EVENT_MULTIPLIER multiplier; // economic indicator value multiplier uint digits; // number of decimal places string source_url; // URL of a source where an event is published string event_code; // event code string name; // event text name in the terminal language (in the current terminal encoding) };
국가 설명 — CalendarCountryById 및 CalendarCountries 함수에서 사용:
struct MqlCalendarCountry { ulong id; // country ID (ISO 3166-1) string name; // country text name (in the current terminal encoding) string code; // country code name (ISO 3166-1 alpha-2) string currency; // country currency code string currency_symbol; // country currency symbol string url_name; // country name used in the mql5.com website URL };
이벤트 값 - CalendarValueById, CalendarValueHistoryByEvent, CalendarValueHistory, CalendarValueLastByEvent 및 CalendarValueLast 함수에서 사용.
struct MqlCalendarValue { ulong id; // value ID ulong event_id; // event ID datetime time; // event date and time datetime period; // event reporting period int revision; // revision of the published indicator relative to the reporting period long actual_value; // actual value in ppm or LONG_MIN if the value is not set long prev_value; // previous value in ppm or LONG_MIN if the value is not set long revised_prev_value; // revised previous value in ppm or LONG_MIN if the value is not set long forecast_value; // forecast value in ppm or LONG_MIN if the value is not set ENUM_CALENDAR_EVENT_IMPACT impact_type; // potential impact on the currency rate //--- functions for checking the values bool HasActualValue(void) const; // returns 'true' if actual_value is set bool HasPreviousValue(void) const; // returns 'true' if prev_value is set bool HasRevisedValue(void) const; // returns 'true' if revised_prev_value is set bool HasForecastValue(void) const; // returns 'true' if forecast_value is set //--- functions for getting values double GetActualValue(void) const; // return actual_value or nan if the value is not set double GetPreviousValue(void) const; // return prev_value or nan if the value is not set double GetRevisedValue(void) const; // returns revised_prev_value or nan if the value is not set double GetForecastValue(void) const; // returns forecast_value or nan if the value is not set };
주의:
참고로, MqlCalendarValue 구조체는 actual_value, forecast_value, prev_value 및 revised_prev_value 필드의 값을 확인하고 가져오는 메서드를 제공합니다. 나열된 필드에 값이 없을 수 있습니다 - 예를 들어 뉴스 항목이 아직 발표되지 않았기 때문에 actual_value 필드가 비어 있을 수 있습니다. 값을 얻는 가장 좋은 방법은 구조체 자체의 메서드를 사용하여 값을 확인하고 가져오는 것입니다.
이 구조체들은 다음과 같은 관계에 따라 서로 연결되어 있습니다:
그림 3. 달력 구조체 관계
MqlCalendarCountry 구조체는 국가 ID를 통해 MqlCalendarEvent 구조체와 연결됩니다. 관계의 형태는 "일대다"(1..*)입니다.
MqlCalendarEvent 구조체는 이벤트 ID를 통해 MqlCalendarValue 구조체 와 연결됩니다. 관계의 형태는 "일대다"(1..*)입니다.
뉴스 발표 시간 및 서버 시간
경제 달력과 관련된 모든 함수는 TimeTradeServer() 거래 서버 시간을 사용합니다. 이는 MqlCalendarValue 구조체의 시간과 CalendarValueHistoryByEvent() 및 CalendarValueHistory() 함수의 시간 입력값이 사용자의 현지 시간이 아닌 거래 서버 시간대로 설정됨을 의미합니다.
변환이 필요하지 않습니다: MqlCalendarValue :: period 이벤트 시간은 TimeCurrent() 또는 TimeTradeServer() 함수를 호출할 때 얻은 시간과 직접 비교할 수 있습니다. 테스터에서 TimeTradeServer() 함수는 과거 데이터의 시간과 동일한 모델 시간을 반환합니다. 시간 창("뉴스 발표 30분 전") 처리 로직은 실시간과 과거 데이터에서 동일하게 작동합니다. 브로커가 서머타임 시간제 전환을 적용하면 달력이 자동으로 조정됩니다.
실제 예시 - 오늘(현재 날짜)의 일정 목록을 가져오는 방법:
//+------------------------------------------------------------------+ //| Get calendar values for the current day | //+------------------------------------------------------------------+ void GetTodayUSD_Events() { //--- define the period boundaries in server time datetime server_now = TimeTradeServer(); datetime day_start = server_now - (server_now % 86400); datetime day_end = day_start + 86400; MqlCalendarValue values[]; MqlCalendarEvent event; MqlCalendarCountry country; //--- request values only for USD if(CalendarValueHistory(values, day_start, day_end, NULL, "USD")) { Print(" Events received for USD: ", ArraySize(values)); //--- iterate over the array of values for(int i = 0; i < ArraySize(values); i++) { //--- get event description if(CalendarEventById(values[i].event_id, event)) { //--- get country description if(CalendarCountryById(event.country_id, country)) { Print("✅ Event #", i); Print("Event ID: ", values[i].event_id); Print("Event name: ", event.name); Print("Sector: ", event.sector); Print("Source: ", event.source_url); Print("Country name: ", country.name); Print("Country URL: ", country.url_name); Print("Time: ", TimeToString(values[i].time, TIME_DATE | TIME_SECONDS)); Print("Impact: ", values[i].impact_type); // CHECK AND OUTPUT VALUES if(values[i].HasActualValue()) Print("Actual: ", values[i].GetActualValue()); if(values[i].HasRevisedValue()) Print("Revised: ", values[i].GetRevisedValue()); if(values[i].HasForecastValue()) Print("Forecast: ", values[i].GetForecastValue()); if(values[i].HasPreviousValue()) Print("Previous: ", values[i].GetPreviousValue()); } } } } else { int error = GetLastError(); if(error == 0) { Print("❌ CalendarValueHistory: No Events"); } else { Print("❌ Error CalendarValueHistory: ", error); } } } //+------------------------------------------------------------------+
이 함수는 현재 날짜의 미국 달러(USD) 통화와 관련한 이벤트 목록과 달력의 MQL API를 통해 가져온 구조체의 주요 필드의 내용을 표시합니다. 전체 스크립트 코드는 기사에 첨부된 GetTodayEvents-S.mq5 파일에 포함되어 있습니다.
함수의 작동 결과는 MetaTrader 5 터미널의 도구 상자 > 시스템 트레이딩 탭에서 확인할 수 있습니다. USD에 대해 두 건의 이벤트가 수신되었습니다. 요청 당시에는 이러한 이벤트들이 아직 발생하지 않은 상태였습니다(뉴스가 아직 발표되지 않은 것입니다). 따라서 MqlCalendarValue :: actual_value 필드에는 값이 없습니다. HasActualValue() 함수는 'false'를 반환합니다.
이러한 이벤트 유형의 경우 HasForecastValue()에서 확인하는 MqlCalendarValue :: forecast_value 필드가 없습니다(값이 포함되어 있지 않습니다). 다른 이벤트의 경우 네 가지 필드(prev_value, actual_value, forecast_value 및 revised_prev_value)가 모두 누락될 수 있습니다.
USD에 대한 수신 이벤트: 2
✅ Event #0
Event ID: 840220005
Event name: 3-month treasury bills auction
Sector: 1
Source: https://home.treasury.gov/
Country name: USA
Country URL: united-states
Time: 2026.04.20 18:30:00
Impact: 0
Previous: 3.62
✅ Event #1
Event ID: 840220006
Event name: 6-month treasury bills auction
Sector: 1
Source: https://home.treasury.gov/
Country name: USA
Country URL: united-states
Time: 2026.04.20 18:30:00
Impact: 0
Previous: 3.61
코드에 대한 설명:
먼저, "USD" 통화 필터를 사용하여 주어진 시간 범위 내의 모든 이벤트 값의 배열을 얻습니다. 반복문 내에서 값 배열을 순회하며 CalendarEventById() 함수를 사용하여 MqlCalendarValue :: event_id 이벤트 ID로 이벤트 설명을 요청합니다. 그런 다음 CalendarCountryById() 함수를 사용하여 MqlCalendarEvent :: country_id로 국가 설명을 요청합니다.
CalendarValueHistory() 함수가 'false'를 반환하지만 GetLastError() 함수가 0(오류 없음)을 반환하는 경우 요청된 설정값을 가진 이벤트가 없다는 것을 나타냅니다.
달력 사용 시 오류 처리 및 제한 사항
원격 데이터를 다루는 것은 항상 연결 끊김이나 접근 제한이라는 위험을 수반합니다. 달력 함수는 작동 불능 시 false 또는 0을 반환합니다. 그 이유를 알기 위해 GetLastError()를 사용해야 합니다. 문서에는 달력 모듈과 관련된 별도의 오류 그룹이 강조 표시되어 있습니다.
오류 코드:
- ERR_CALENDAR_TIMEOUT (code 5200) — 서버 응답 대기 시간이 만료되었습니다. 네트워크 오류 또는 서버 과부하. 해결책: 5~10초 정도 기다린 후 다시 요청하세요.
- ERR_CALENDAR_NO_DATA (code 5201) — 달력 데이터가 아직 로드되지 않았습니다. 달력은 비동기식으로 초기화됩니다. 해결책: 1~2초 정도 기다렸다가 반복하세요.
- ERR_CALENDAR_INVALID_DATE (code 5202) — 잘못된 날짜 범위입니다. 코드 오류(예: 시작 날짜가 종료 날짜보다 큼). 해결책: 논리를 수정하세요. 요청을 반복하는 것은 소용없습니다.
- ERR_CALENDAR_INVALID_COUNTRY (code 5203) — 알 수 없는 국가/통화 코드입니다. 요청 매개변수에 오류가 있습니다. 해결책: 코드를 확인하세요(예: "USA" 대신 "US").
- ERR_CALENDAR_TOO_MANY_REQUESTS (code 5204) — 요청 제한을 초과했습니다. 치명적인 오류. 해결책: 요청 간격을 늘리세요.
요청 속도 제한:
MetaTrader 5 터미널이 연결되는 서버는 서버 인프라를 과부하로부터 보호합니다. EA가 매 틱마다 또는 지연 없이 루프 내에서 CalendarValueHistory()를 호출하면 서버에서 오류 5204가 반환되고 터미널의 달력 접근이 일시적으로 차단됩니다. 모범 사례: 필요한 기간 동안의 데이터를 시작 시 OnInit() 메서드에서 한 번만 로드 합니다.
실시간 업데이트를 위해서는 CalendarValueLast()를 사용하고 반환된 change_id 변수를 저장하세요. 이렇게 하면 전체 데이터 배열이 아닌 변경 사항만 수신할 수 있습니다. OnTimer() 함수에서 타이머 데이터를 5~10분 이내의 간격으로 업데이트합니다.
달력을 불러올 때 발생할 수 있는 문제에 대해 몇 가지 해결책을 살펴보겠습니다.
터미널을 처음 실행(콜드 스타트)하거나 EA를 시작할 때 달력이 바로 준비되지 않을 수 있습니다. 데이터는 백그라운드에서 서버로부터 로드 됩니다. OnInit() 함수의 첫 번째 줄에서 CalendarValueHistory() 함수를 호출하면 데이터 없음 오류인 5201이 발생할 가능성이 높습니다. 이럴 경우 지수적 또는 고정 타임아웃을 사용하는 준비 상태 폴링 메커니즘을 구현해야 합니다.
실제 예시 - 오류 처리 기능이 포함된 달력 데이터 불러오기 함수:
//+------------------------------------------------------------------+ //| Calendar loading function | //+------------------------------------------------------------------+ bool LoadCalendar(MqlCalendarValue& values[], const datetime from, const datetime to, const string country_code = NULL, const string currency = NULL, const int max_retries = 5) { int retry_count = 0; while(retry_count < max_retries) { ResetLastError(); //--- download attempt if(CalendarValueHistory(values, from, to, country_code, currency)) return true; // Success int error = GetLastError(); //--- in case of "No data" (5201) or "Timeout" (5200) error — wait and repeat if(error == 5201 || error == 5200) { retry_count++; Sleep(1000); // 1 second pause before repeating continue; } //--- if the error is critical (for example, an invalid date), interrupt the download immediately Print("❌ Critical Calendar Error: ", error); return false; } Print("❌ Failed to load calendar after ", max_retries, " attempts."); return false; } //+------------------------------------------------------------------+
코드에 대한 설명:
"데이터 없음"(5201) 및 "시간 초과"(5200) 오류가 발생할 가능성을 고려하세요. 이것들은 수정 가능한 오류들입니다. 1~5초 정도 멈췄다가 데이터를 다시 요청하는 방식으로 처리하세요. 복구할 수 없는 오류가 발생하면 즉시 다운로드를 중지하세요. 복구 불가능한 오류에는 잘못된 날짜, 잘못된 통화 또는 국가 코드 등이 포함됩니다. 전체 스크립트 코드는 이 문서에 첨부된 GetTodayEvents-S.mq5 파일에서 확인할 수 있습니다.
제한 사항에 걸리지 않고 최대의 반응 속도를 확보하려면 OnTimer()에서 change_id 메커니즘을 사용해야 합니다. 시작 시 전체 이벤트 기록을 불러오고 마지막 change_id를 기억합니다. 타이머에서 CalendarValueLast() 함수를 사용하여 새로운 데이터만 요청하세요. 서버는 변경된 내용만 반환하고(변경 사항이 없으면 'false'를 반환) 이전 데이터를 전송하는 데 리소스를 낭비하지 않을 것입니다.
실제 예시 - 오류 처리 기능이 포함된 달력 데이터 업데이트 함수:
//+------------------------------------------------------------------+ //| Timer: incremental update | //+------------------------------------------------------------------+ void OnTimer() { if(!is_initialized) return; MqlCalendarValue updates[]; ResetLastError(); //--- API automatically updates last_change_id by reference if(!CalendarValueLast(last_change_id, updates, (InpCountryCode == "") ? NULL : InpCountryCode, (InpCurrency == "") ? NULL : InpCurrency)) { int err = GetLastError(); // 0 = SUCCESS/NO_NEW_DATA, 5402 = ERR_CALENDAR_NO_CHANGES if(err != 0 && err != 5402) Print("🔴️ CalendarValueLast error: ", err); return; } int cnt = ArraySize(updates); if(cnt == 0) return; Print("🟢 Received ", cnt, " updates. New change_id: ", last_change_id); if(InpPrintChanges) ArrayPrint(updates); SyncCache(updates); } //+------------------------------------------------------------------+ //| Cache synchronization | //+------------------------------------------------------------------+ void SyncCache(const MqlCalendarValue &updates[]) { int upd_cnt = ArraySize(updates); int cache_sz = ArraySize(calendar_cache); for(int u = 0; u < upd_cnt; u++) { bool found = false; for(int c = 0; c < cache_sz; c++) { if(calendar_cache[c].id == updates[u].id) { calendar_cache[c] = updates[u]; found = true; break; } } if(!found) { ArrayResize(calendar_cache, cache_sz + 1); calendar_cache[cache_sz] = updates[u]; cache_sz++; total_events++; } } } //+------------------------------------------------------------------+
제공된 코드에 대한 설명:
이벤트의 점진적인 업데이트는 OnTimer() 타이머의 이벤트 핸들러를 통해 이루어집니다. 달력 API의 CalendarValueLast() 함수가 호출됩니다. 새 이벤트가 발생하면 이전에 로드된 MqlCalendarValue 구조체 배열의 해당 요소가 업데이트 됩니다. 그리고 데이터를 터미널 저널에 기록합니다. 로드된 배열에서 해당 이벤트를 찾을 수 없는 경우 해당 이벤트가 배열에 추가됩니다.
전체 스크립트 코드는 이 글에 첨부된 CalendarEventMonitor-EA.mq5 파일에 있습니다.
이벤트 필터링: 일반적인 것에서 구체적인 것으로
필터를 사용하는 이유:
경제 달력에는 매일 60~90개의 이벤트가 발표됩니다. 마치 시장에 즉각적인 영향을 미치지 않는 수백 가지의 작은 지표, 공휴일, 공식 연설이 끊임없이 쏟아지는 것을 듣고 있는 것과 같습니다. 이들 각각에 대해 거래하는 것은 과도한 매매로 이어져 예치금을 잃는 지름길이 됩니다. 뉴스 알고리즘 트레이더의 임무는 시장에 실질적인 영향을 미치는 3~5개의 이벤트를 선별하는 것입니다.
알고리즘 뉴스 거래에서 필터링은 자동화된 의사 결정에 적합한 수준으로 신호 대 잡음의 비율을 높이는 것과 같습니다. MQL5 달력 API에서 이 프로세스는 MqlCalendarEvent 및 MqlCalendarValue 구조체 배열이 통과하는 다단계 선별 시스템으로 구현됩니다.
필터링 기준:
시장은 뉴스 발표 자체에 반응하는 것이 아닙니다. 시장의 반응은 실제 거시경제적 영향 수준에서의 데이터와 예측치 사이의 차이에 반응합니다. 영향력이 낮은 이벤트( MqlCalendarValue :: impact_type < 3)는 거래 알고리즘과 마켓 메이커에게 종종 무시됩니다.
필터링을 하지 않으면 다음과 같은 결과가 나옵니다:
- 보조 통계에 대해 잘못된 전략 트리거;
- 스프레드가 넓고 변동성이 적은 기간 동안의 거래;
- EA의 로직에 불필요한 검사를 과도하게 추가.
필터링의 목표는 데이터 흐름을 90% 줄여 트레이더가 선택한 대상 통화에 대해 주어진 시간 범위 내에서 영향력이 큰 이벤트( ENUM_CALENDAR_EVENT_IMPORTANCE :: CALENDAR_IMPORTANCE_HIGH)만 남기는 것입니다.
실시간 모드와 테스트 모드 모두에서 동일하게 안정적으로 작동하는 신뢰할 수 있는 필터를 구축하는 방법을 살펴보겠습니다.
다단계 필터 시스템:
터미널에서 다운로드한 "원시" 이벤트 데이터는 가공되지 않은 광석과 같습니다: 중요하지 않은 통계 보고서부터 알고리즘 전략에 아무런 도움이 되지 않는 공휴일에 이르기까지 수천 개의 항목이 포함되어 있습니다.
이 데이터 스트림을 깨끗한 신호로 변환하려면 다단계 필터링 시스템이 필요합니다. 이벤트 필터링은 원시 데이터를 통과시키는 "체"의 역할을 하여 지정한 기준(통화, 중요도, 코드 및 시간 범위)을 충족하는 이벤트만 출력에 남깁니다.
효과적인 필터는 계층적 구조를 가져야 합니다: 즉 지리적 구분으로 초기 대략적인 차단으로 시작하여 시간에 따른 세밀한 조정으로 마무리되어야 합니다.
뉴스 MQL5-API의 중요한 특징 중 하나는 MqlCalendarValue 구조체에 currency_id 및 country_id 필드가 포함되어 있지 않다는 점입니다. event_id, 시간, 지표 값 및 영향 유형만 저장합니다. 이것이 필터링에 어떤 영향을 미칠까요?
통화 필터링은 API 요청 수준에서 수행됩니다. CalendarValueHistory (..., NULL, "USD")를 호출하면 터미널은 USD를 제외한 모든 값을 버립니다. 그리고 수신한 배열은 이미 통화별로 필터링 되어 있습니다. 따라서 currency_id를 실시간으로 확인할 필요 없이 시간, 중요도, 예측 존재 여부/의미를 기준으로 필터링하면 충분합니다.
레벨 1: 통화 필터
첫 번째 장벽은 이벤트 지역입니다. EURUSD을 매매하고 있는 동안에는 EA는 유로존(EUR)과 미국(USD)에 영향을 미치는 이벤트에만 반응해야 합니다. 필터링은 거래된 금융 상품의 통화 코드를 사용하여 이벤트를 요청하는 것이어야 합니다. 교차율의 경우 (예: "GBPJPY"의 경우 두 통화("GBP"와 "JPY") 및 "USD"에 대한 이벤트를 요청해야 합니다.
레벨 2: 이벤트 중요도
모든 뉴스는 경제, 나아가 물가에 미치는 영향이라는 측면에서 중요도가 각기 다릅니다 - 결국 가격에 영향을 미칩니다. 소비자물가지수(CPI)는 시장을 100포인트까지 움직일 수 있는 반면 ZEW 소비자 신뢰지수는 시장을 10포인트만 움직일 수 있습니다.
MQL5-API는 중요도 설정을 위해 ENUM_CALENDAR_EVENT_IMPORTANCE 열거형을 사용합니다:
enum ENUM_CALENDAR_EVENT_IMPORTANCE { CALENDAR_IMPORTANCE_NONE, // importance level is not set CALENDAR_IMPORTANCE_LOW, // low importance CALENDAR_IMPORTANCE_MODERATE, // medium importance CALENDAR_IMPORTANCE_HIGH // high importance };
뉴스 EA는 유동성이 낮은 뉴스에 대한 오탐을 방지하기 위해 HIGH 미만의 모든 것을 무시해야 합니다.
레벨 3: 이벤트 코드
아무리 중요한 사건이라도 예외는 존재합니다. 예를 들어 유럽중앙은행(ECB)이나 국제통화기금(IMF)의 연설은 중요하다고 표시될 수 있지만 구체적인 수치가 발표되는 경우 보다 알고리즘적으로 처리하기가 더 어렵습니다. 따라서 이벤트 코드별로 정렬해야 합니다: MqlCalendarEvent :: event_code 필드에는 고유 ID(예: "NONFARM", "CPI", "GDP")가 포함되어 있습니다. 우리는 여론조사나 연설이 아닌 경제 수치가 포함된 이벤트만 선정합니다.
처음 세 단계의 검증을 통과한 후에야 이벤트가 결과 배열에 복사되어 매매에 사용됩니다.
그림 4. 뉴스 트레이딩에서 성공하려면 다단계 필터링이 핵심입니다.
첫 세 단계의 필터링을 거친 후 EA는 이벤트가 구성된 시간 범위 창에 속하는지 확인합니다. 이는 트레이딩 EA가 실행 중일 때 일어납니다.
레벨 4: 시간 창
마지막 단계는 관련성을 확인하는 것입니다. 한 달 후에 나올 뉴스는 지금 필요하지 않습니다. 어제 나온 소식은 이미 시장에 반영되었습니다. 따라서 TimeCurrent()가 뉴스 발표 전 지정된 시간과 뉴스 발표 후 지정된 시간 사이의 간격에 포함되면 이벤트는 활성 상태로 간주됩니다. 일반적으로 다음과 같은 설정이 사용됩니다: 발표 15~30분 전 - 포지션 청산 시, 발표 60분 후 - 변동성 및 가격 움직임 방향 분석 시.
MQL5 바이너리 리소스: 효율적인 캐싱을 위해 이벤트를 오프라인 테스트로 이전.
뉴스 기반 알고리즘 트레이딩 테스트의 문제점은 전략 테스터가 인터넷에 접속할 수 없다는 점입니다. 이 솔루션은 테스트 속도를 높이고 비결정적 요인들으로부터 보호하지만 뉴스 기반 전략을 테스트하는 것에는 문제를 야기합니다.
테스터의 EA가 CalendarValueHistory() 함수를 호출하면 오류가 발생하고 비어 있는 배열이 반환됩니다. 이 전략을 테스트하기 위해서는 과거 뉴스 데이터를 .ex5 실행 파일에 삽입해야 합니다. 이를 위해 MQL5 바이너리 리소스를 사용하는 옵션을 활용합니다. 왜 하필 이 옵션을 선택했을까요? 이는 구조화된 데이터에 접근하는 가장 빠른 방법입니다 - 접근 속도는 오직 컴퓨터의 파일 시스템 성능에 달려 있습니다.
MqlCalendarValue[] 배열을 압축된 바이너리 파일로 변환하고 테스터가 RAM에서 데이터를 초고속으로 읽을 수 있도록 코드에 삽입하는 방법을 살펴보겠습니다. 첫 번째 단계는 내보내기 스크립트를 만드는 것입니다. 이 스크립트는 달력에서 현재 데이터를 다운로드하여 바이너리 파일에 저장합니다.
실용적 예 - 이벤트를 바이너리 파일로 내보내는 스크립트:
전체 스크립트 코드는 이 글에 첨부된 ExportCalendarForTester.mq5 파일에 있습니다.
1단계: 배열 구성 및 필터링
우리는 전 세계에서 일어나는 모든 사건을 해당 리소스에 저장하지는 않을 것입니다. 바이너리 파일로 내보내는 단계에서는 EA에서와 동일한 필터(통화, 중요도, 이벤트 유형)를 적용합니다. 스크립트 입력값에는 통화 목록, 이벤트 로딩 시간 간격, 이벤트 코드 및 최소 중요도가 지정됩니다.
이벤트 코드 목록(InpEventCodes 매개변수)은 기본적으로 비어 있습니다 - 모든 이벤트 유형이 로드된다는 것입니다. 우리는 이 필터를 좁힐 수 있습니다. 예를 들어 "농업이 아닌 것"의 경우 코드 목록은 "NONFARM"이 됩니다.
InpUseCommonDir 플래그는 바이너리 파일을 저장할 위치를 지정합니다. true인 경우 모든 클라이언트 터미널에서 공유하는 폴더 인 "\Terminal\Common\Files"에 저장합니다.
//--- input parameters input string InpCurrencies = "USD"; input datetime InpDateFrom = D'2025.01.01'; input datetime InpDateTo = 0; input string InpEventCodes = ""; input ENUM_CALENDAR_EVENT_IMPORTANCE InpMinImportance = CALENDAR_IMPORTANCE_HIGH; input string InpOutputFile = "calendar_test_res.bin"; input bool InpUseCommonDir = true;
입력값에 지정된 값으로 이벤트를 불러오고 필터링하는 스크립트의 일부는 다음과 같습니다:
Print("🔄 Calendar export:"); Print("----------------------------------"); Print("Filtering options:"); Print(" 🟨 Interval = ", InpDateFrom, " — ", InpDateTo); Print(" 🟨 Currencies = ", InpCurrencies, "\n 🟨 Event Codes = ", InpEventCodes, "\n 🟨 Min Importance = ", EnumToString(InpMinImportance)); Print("----------------------------------"); //--- initialize filter by currencies ArrayResize(currencies, 0); if(InpCurrencies == "") return; StringSplit(InpCurrencies, ',', currencies); currencies_size = ArraySize(currencies); for(int i = 0; i < currencies_size; i++) StringToUpper(currencies[i]); //--- initialize the filter by event codes ArrayResize(event_codes, 0); if(InpEventCodes != "") StringSplit(InpEventCodes, ',', event_codes); event_codes_Size = ArraySize(event_codes); //--- load calendar values with the CURRENCY FILTER (if specified) if(currencies_size > 0) { ArrayResize(values_size, currencies_size); ArrayFill(values_size, 0, currencies_size, 0); for(int i = 0; i < currencies_size; i++) { if(LoadCalendar(raw_values, InpDateFrom, InpDateTo, "", currencies[i])) { raw_values_size = ArraySize(raw_values); //--- load events with an IMPORTANCE AND EVENT CODE FILTER for(int k = 0; k < raw_values_size; k++) { //--- get event description if(CalendarEventById(raw_values[k].event_id, event)) { //--- take indicators only if(event.type != CALENDAR_TYPE_INDICATOR) continue; // nothing else to filter //--- check by IMPORTANCE if(event.importance < InpMinImportance) continue; // nothing else to filter //--- check by EVENT CODE (if specified) if(event_codes_Size > 0) { bool code_allowed = false; for(int c = 0; c < event_codes_Size; c++) { StringToUpper(event.event_code); if(StringFind(event.event_code, event_codes[c]) >= 0) { code_allowed = true; break; } } if(code_allowed == false) continue; } //--- replenish the array of filtered events int event_index = ArraySize(values); ArrayResize(values, event_index + 1); values[event_index] = raw_values[k]; values_size[i]++; } } Print("✅ Received values BY CURRENCY \"", currencies[i], "\": ", raw_values_size, " → Of these, filtered: ", values_size[i]); } else { int error = GetLastError(); if(error == 0) Print("⚠ LoadCalendar Info: No Events for ", currencies[i]); else Print("❌ LoadCalendar Error: ", error, " for ", currencies[i]); return; // nothing else to filter } } }
코드에 대한 설명:
- values_size[] 배열은 inputs에 지정된 각 통화 코드에 대해 필터링 된 이벤트 수를 저장합니다.
- MQL5 datetime 타입의 0 값은 1970년 1월 1일 00시 00분 00초에 해당합니다.
- 이벤트 유형별로 추가 필터링이 수행됩니다. 오직 ENUM_CALENDAR_EVENT_TYPE :: CALENDAR_TYPE_INDICATOR 유형의 이벤트가 정렬됩니다 - 이는 경제 이벤트(연설이 아닌 수치)입니다. 그 외의 모든 뉴스는 차단됩니다. 이는 필터링 시작 단계에서 유효성 검사를 통해 이루어집니다:
//--- take only indicators if(event.type != CALENDAR_TYPE_INDICATOR) continue; // Nothing else to filter
다양한 필터링 매개변수 세트를 사용하여 스크립트를 실행해 보고 이벤트 로딩/필터링 스크립트가 얼마나 빠르게 작동하는지 확인해 보겠습니다. 속도는 컴퓨터의 성능과 인터넷을 통한 데이터 서버 연결 속도에 따라 달라진다는 것이 분명합니다. 그럼에도 불구하고 우리는 스트레스 테스트를 실시할 것입니다. 결과는 아래와 같습니다.
USD 관련 가능한 모든 중요한 이벤트 — 이용 가능한 모든 과거 데이터를 통틀어:
13:59:43.724 🔄 Calendar export:
13:59:43.724 ----------------------------------
13:59:43.724 Filtering options:
13:59:43.724 🟨 interval = 1970.01.01 00:00:00 — 1970.01.01 00:00:00
13:59:43.724 🟨 Currencies = USD
13:59:43.724 🟨 Event Codes =
13:59:43.724 🟨 Min Importance = CALENDAR_IMPORTANCE_HIGH
13:59:43.724 ----------------------------------
13:59:45.511 ✅ Received values BY CURRENCY "USD": 53346 → 이 중 필터링된 항목: 9009
13:59:45.511 ----------------------------------
13:59:45.513 ✅ Saved: USD_calendar_test_res.bin Size: 1153152바이트(9009개 이벤트)
필터링된 값 배열( MqlCalendarValue structures)을 얻는 데 걸린 시간: 1.80초(필터당 0.20ms).
이용 가능한 모든 과거 데이터에서 중요도가 높은 모든 USD 및 JPY 통화 :
14:03:08.724 🔄 Calendar export:
14:03:08.724 ----------------------------------
14:03:08.724 Filtering options:
14:03:08.724 🟨 Interval = 1970.01.01 00:00:00 — 1970.01.01 00:00:00
14:03:08.724 🟨 Currencies = USD,EUR,JPY
14:03:08.724 🟨 Event Codes =
14:03:08.724 🟨 Min Importance = CALENDAR_IMPORTANCE_HIGH
14:03:08.724 ----------------------------------
14:03:10.488 ✅ Received values BY CURRENCY "USD": 53346 → 이 중 필터링된 항목: 9009
14:03:10.947 ✅ Received values BY CURRENCY "EUR": 45139 → 이 중 필터링된 항목: 1102
14:03:11.404 ✅ Received values BY CURRENCY "JPY": 18907 → 이 중에서 필터링된 항목: 937
14:03:11.404 ----------------------------------
14:03:11.406 ✅ Saved: USD_calendar_test_res.bin Size: 1153152바이트(9009개 이벤트)
14:03:11.407 ✅ Saved: EUR_calendar_test_res.bin Size: 141056 bytes (1102 events)
14:03:11.408 ✅ Saved: JPY_calendar_test_res.bin Size: 119936 bytes (937 events)
필터링된 값 배열( MqlCalendarValue structures)을 얻는 데 걸린 시간: 2.68초(필터당 0.24ms).
중요도가 높은 NFP(비농업 부문) 지표( USD 기준 ) — 이용 가능한 모든 과거 데이터를 통틀어:
14:07:22.203 ----------------------------------
14:07:22.203 Filtering options:
14:07:22.203 🟨 Interval = 1970.01.01 00:00:00 — 1970.01.01 00:00:00
14:07:22.203 🟨 Currencies = USD
14:07:22.203 🟨 Event Codes = NONFARM
14:07:22.203 🟨 Min Importance = CALENDAR_IMPORTANCE_HIGH
14:07:22.203 ----------------------------------
14:07:22.290 ✅ Received values BY CURRENCY "USD": 53346 → 이 중 필터링된 항목: 473
14:07:22.290 ----------------------------------
14:07:22.291 ✅ Saved: USD_calendar_test_res.bin Size: 60544 bytes (473 events)
필터링된 값 배열( MqlCalendarValue structures)을 얻는 데 걸린 시간: 0.09초(필터당 0.18ms).
비고:
- 필터에 전체 이벤트 코드를 지정할 필요는 없습니다. 코드를 포함한 문자열의 고유한 부분만 지정하면 충분합니다. 예를 들어 "비농업"의 경우 전체 코드는 "NONFARM-PAYROLLS"입니다 - "NONFARM"만 지정하면 됩니다.
- MetaTrader 5 터미널에서 실시간 거래 시스템을 실행할 때 전체 기간의 모든 이벤트를 다운로드해야 할 필요는 없을거 같습니다. 일반적으로 현재 이벤트는 당일과 다음 주에 대한 내용을 다운로드합니다. 따라서 뉴스 EA 알고리즘에서 로딩 시간은 무시해도 됩니다.
2단계: 이벤트 배열을 바이너리 파일로 내보냅니다.
SaveToBinary 함수는 필터링 된 이벤트를 바이너리 파일에 저장합니다. 파일의 수는 입력값에 지정된 통화 코드의 수와 같습니다 - 각 통화는 별도의 바이너리 파일로 내보내집니다. 파일 이름은 통화 코드 접두사로 시작합니다. 예를 들어, "USD_calendar_test_res.bin"과 같습니다. 이렇게 하면 통화 코드와 해당 통화에 대한 이벤트 목록 사이에 일대일 대응 관계를 얻을 수 있습니다.
//+------------------------------------------------------------------+ //| Save an array to a binary file | //+------------------------------------------------------------------+ void SaveToBinary(MqlCalendarValue &values[], const int &vls_size[], const string filename, const string ¤cies[]) { if(ArraySize(values) == 0) { Print("⚠️ Nothing to save"); return; } Print("----------------------------------"); int offset = 0; for(int i = 0; i < ArraySize(vls_size); i++) { int file_handle = FileOpen(currencies[i] + "_" + filename, FILE_WRITE | FILE_BIN | (InpUseCommonDir ? FILE_COMMON : 0)); if(file_handle == INVALID_HANDLE) { Print("❌ FileSave failed: ", GetLastError()); return; } FileWriteArray(file_handle, values, offset, vls_size[i]); FileFlush(file_handle); FileClose(file_handle); Print("✅ Saved: ", currencies[i] + "_" + filename, " Size: ", vls_size[i] * sizeof(MqlCalendarValue), " bytes", " (", vls_size[i], " events)"); offset += vls_size[i]; } }
이벤트를 바이너리 파일로 내보내는 스크립트를 성공적으로 실행한 후 MetaTrader 5의 Expert 탭에 다음과 같은 메시지가 나타납니다.
14:28:42.077 ----------------------------------
14:28:42.078 ✅ Saved: USD_calendar_test_res.bin Size: 58240 bytes (455 events)
14:28:42.079 ✅ Saved: EUR_calendar_test_res.bin Size: 7680 bytes (60 events)
3단계: 리소스를 EA로 컴파일
MQL5/Files/에서 ExportCalendarForTester.mq5 스크립트를 실행하면 USD_calendar_test_res.bin 및 EUR_calendar_test_res.bin 파일이 나타납니다. 이제 이 파일들을 EA에 "임베드"해야 합니다. EA 파일의 시작 부분(#property 지시문 뒤)에 다음 형식의 줄을 하나(단일 통화용 파일을 사용하는 경우) 또는 몇 개 추가하세요.
// Embed the binary file as a static resource at the compilation stage #resource "\\Files\\USD_calendar_test_res.bin" as MqlCalendarValue USD_res_calendar_data[] #resource "\\Files\\EUR_calendar_test_res.bin" as MqlCalendarValue EUR_res_calendar_data[]
주의:
- #리소스의 경로는 MQL5/Files/ 폴더를 기준으로 상대 경로로 지정됩니다. \\Files\\ 접두사는 필수입니다.
- EA를 컴파일할 때 MetaEditor의 오류 탭에 다음과 같은 메시지가 나타나야 합니다:
- 'USD_calendar_test_res.bin' as 'const MqlCalendarValue USD_res_calendar_data[455]'
- 'EUR_calendar_test_res.bin' as 'const MqlCalendarValue EUR_res_calendar_data[60]'
저장된 이벤트 구조는 EA를 시작할 때 내보내기 단계에서USD_res_calendar_data [] 및 EUR_res_calendar_data [] 배열에 저장됩니다.
EA로의 통합: 자동 모드 전환
목표는 동일한 EA 코드가 실시간 환경과 테스터 환경 모두에서 작동하고 데이터 소스를 자동으로 선택하도록 하는 것입니다. MQLInfoInteger (MQL_TESTER) 함수는 EA가 전략 테스터 또는 최적화 도구에서 실행 중인 경우 'true'를 반환합니다.
//+------------------------------------------------------------------+ //| Initialization: select a data source | //+------------------------------------------------------------------+ int OnInit() { Print("🔄 Initializing the news module:"); //--- initialize filter by currencies ArrayResize(currencies, 0); if(InpCurrencies == "") return INIT_FAILED; StringSplit(InpCurrencies, ',', currencies); currencies_size = ArraySize(currencies); for(int i = 0; i < currencies_size; i++) StringToUpper(currencies[i]); //--- initialize the filter by event codes ArrayResize(event_codes, 0); if(InpEventCodes != "") StringSplit(InpEventCodes, ',', event_codes); event_codes_Size = ArraySize(event_codes); //--- define the execution environment bool is_tester = MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_VISUAL_MODE); if(is_tester) { //--- TESTER MODE: Load from resource if(!LoadFromResource()) { Print("❌ ERROR: Failed to load calendar from resource"); return INIT_FAILED; } is_live_mode = false; Print("⚠️ Mode: TESTER (data from resource)"); } else { //--- LIVE MODE: Load from API if(!LoadFromCalendarAPI()) { int error = GetLastError(); Print("❌ ERROR: Failed to load calendar from API - ", error); return INIT_FAILED; } is_live_mode = true; Print("⚠️ Mode: LIVE (data from API)"); } return INIT_SUCCEEDED; }
코드에 대한 설명:
검사는 EA 초기화 이벤트 핸들러 인 OnInit()에서 수행됩니다.
//--- define the execution environment bool is_tester = MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_VISUAL_MODE); if(is_tester) { //--- TESTER MODE: Load from resource if(!LoadFromResource()) { ... } } else { //--- LIVE MODE: Load from API if(!LoadFromCalendarAPI()) { ... } }
코드에 대한 설명:
실행 중인 MQL5 프로그램(여기서는 EA)이 어떤 모드로 어디에서 작동하는지 결정합니다. 우리는 다음과 같은 플래그에 관심이 있습니다:
- MQL_TESTER — 테스터에서 실행 중인 프로그램의 플래그;
- MQL_OPTIMIZATION — 최적화 중에 실행되는 프로그램의 플래그;
- MQL_VISUAL_MODE — 시각적 테스트 모드에서 실행된 프로그램의 플래그.
14:42:18.894 🔄 Initializing news module:
14:42:18.905 ✅ Events from Server Loaded: 다음 항목에 대한 455개의 이벤트: USD
14:42:18.914 ✅ Events from Server Loaded: 다음 60개 이벤트: EUR
14:42:18.914 ⚠️ Mode: 실시간 (API에서 가져온 데이터)
데이터 변환 체인 검증: 이벤트 → 바이너리 파일 → 리소스로 컴파일 → 리소스에서 이벤트 가져오기
이제 남은 것은 테스터에서 이벤트 검색이 제대로 되는지 확인하고 실시간으로 이 이벤트들이 수신한 목록과 완전히 일치하는지 확인하는 것뿐입니다. 이를 통해 우리는 뉴스 트레이딩 알고리즘의 자동 테스트 및 최적화의 가능성을 확인할 수 있을 것입니다.
이렇게 하려면 리소스의 server/downloaded에서 수신한 이벤트 출력을 terminal/strategy tester log에 추가하세요. 테스터 실행 결과가 포함된 ImportTesterLog.txt 파일 전체를 아래에 첨부했습니다. 아래는 로그 파일의 일부입니다 - 각 통화 코드별 이벤트 배열의 시작과 끝 부분입니다.
전략 테스터에서 단일 실행 결과 (비주얼 모드):
16:48:22.527 InpCurrencies=USD,EUR
16:48:22.527 InpDateFrom=1735689600
16:48:22.527 InpDateTo=1767225600
16:48:22.527 InpEventCodes=
16:48:22.527 InpMinImportance=3
16:48:22.546 🔄 Initializing news module:
16:48:22.546 ✅ Events from Resource Loaded: 다음 항목에 대한 455개의 이벤트: USD
16:48:22.546 ✅ Event #0
16:48:22.546 ID: 230215
16:48:22.546 Event ID: 840140001
16:48:22.546 Time: 2025.01.02 16:30:00
16:48:22.546 Impact: CALENDAR_IMPACT_POSITIVE
16:48:22.546 Revision: 0
16:48:22.546 Actual: 211.0
16:48:22.546 Revised: 220.0
16:48:22.546 Forecast: 219.0
16:48:22.546 Previous: 219.0
16:48:22.546 ✅ Event #1
16:48:22.546 ID: 230641
16:48:22.546 Event ID: 840500001
16:48:22.546 Time: 2025.01.02 17:45:00
16:48:22.546 Impact: CALENDAR_IMPACT_NEGATIVE
16:48:22.546 Revision: 3
16:48:22.546 Actual: 49.4
16:48:22.546 Forecast: 50.5
16:48:22.546 Previous: 49.7
...
16:48:22.553 ✅ Event #453
16:48:22.553 ID: 274419
16:48:22.553 Event ID: 840510001
16:48:22.553 Time: 2025.12.30 17:45:00
16:48:22.553 Impact: CALENDAR_IMPACT_POSITIVE
16:48:22.553 Impact: 0
16:48:22.553 Actual: 43.5
16:48:22.553 Forecast: 42.4
16:48:22.553 Previous: 36.3
16:48:22.553 ✅ Event #454
16:48:22.553 ID: 274484
16:48:22.553 Event ID: 840140001
16:48:22.553 Time: 2025.12.31 16:30:00
16:48:22.553 Impact: CALENDAR_IMPACT_POSITIVE
16:48:22.553 Impact: 0
16:48:22.553 Actual: 199.0
16:48:22.553 Revised: 215.0
16:48:22.553 Forecast: 227.0
16:48:22.553 Previous: 214.0
...
16:48:22.553 ✅ Events from Resource Loaded: 다음 60개 이벤트: EUR
16:48:22.553 ✅ Event #0
16:48:22.553 ID: 231322
16:48:22.553 Event ID: 999030013
16:48:22.553 Time: 2025.01.07 13:00:00
16:48:22.553 Impact: CALENDAR_IMPACT_NA
16:48:22.553 Impact: 1
16:48:22.553 Actual: 2.4
16:48:22.553 Forecast: 2.4
16:48:22.553 Previous: 2.2
16:48:22.553 ✅ Event #1
16:48:22.553 ID: 187384
16:48:22.553 Event ID: 999040007
16:48:22.553 Time: 2025.01.08 13:00:00
16:48:22.553 Impact: CALENDAR_IMPACT_POSITIVE
16:48:22.553 Impact: 0
16:48:22.553 Actual: 21.0
16:48:22.553 Revised: 17.8
16:48:22.553 Forecast: 15.7
16:48:22.553 Previous: 17.7
...
16:48:22.555 ✅ Event #58
16:48:22.555 ID: 204746
16:48:22.555 Event ID: 999010006
16:48:22.555 Time: 2025.12.18 16:15:00
16:48:22.555 Impact: CALENDAR_IMPACT_NA
16:48:22.555 Revision: 0
16:48:22.555 Actual: 2.0
16:48:22.555 Previous: 2.0
16:48:22.555 ✅ Event #59
16:48:22.555 ID: 204762
16:48:22.555 Event ID: 999010007
16:48:22.555 Time: 2025.12.18 16:15:00
16:48:22.555 Impact: CALENDAR_IMPACT_NA
16:48:22.555 Revision: 0
16:48:22.555 Actual: 2.15
16:48:22.555 Previous: 2.15
16:48:22.555 ⚠️ Mode: TESTER (data from resource)
모든 이벤트 배열이 리소스에서 약 10ms 만에 로드되는 것은 분명하며 이는 놀라운 일이 아닙니다 - 해당 리소스는 EA 코드에 내장되어 있으며 테스트가 시작될 때 함께 로드 됩니다.
MetaTrader 5 터미널에서 라이브 모드로 EA 거래를 실행(시작)하면 로드된 이벤트 목록이 포함된 유사한 로그 파일이 생성됩니다. 실시간 로딩 결과가 담긴 LoadLiveLog.txt 파일 전체가 이 글에 첨부되어 있습니다. 아래는 로그 파일의 일부입니다 - 각 통화 코드별 이벤트 배열의 시작과 끝 부분입니다.
서버에서 실시간으로 다운로드한 이벤트 결과:
17:27:25.250 🔄 Initializing news module:
17:27:25.258 ✅ Received values BY CURRENCY "USD": 3589 → 이 중 필터링된 항목: 455
17:27:25.258 ✅ Events from Server Loaded: 다음 항목에 대한 455개의 이벤트: USD
17:27:25.258 ✅ Event #0
17:27:25.258 ID: 230215
17:27:25.258 Event ID: 840140001
17:27:25.258 Time: 2025.01.02 16:30:00
17:27:25.258 Impact: CALENDAR_IMPACT_POSITIVE
17:27:25.258 Revision: 0
17:27:25.258 Actual: 211.0
17:27:25.258 Revised: 220.0
17:27:25.258 Forecast: 219.0
17:27:25.258 Previous: 219.0
17:27:25.258 ✅ Event #1
17:27:25.258 ID: 230641
17:27:25.258 Event ID: 840500001
17:27:25.258 Time: 2025.01.02 17:45:00
17:27:25.258 Impact: CALENDAR_IMPACT_NEGATIVE
17:27:25.258 Revision: 3
17:27:25.258 Actual: 49.4
17:27:25.258 Forecast: 50.5
17:27:25.258 Previous: 49.7
...
17:27:25.288 ✅ Event #453
17:27:25.288 ID: 274419
17:27:25.288 Event ID: 840510001
17:27:25.288 Time: 2025.12.30 17:45:00
17:27:25.288 Impact: CALENDAR_IMPACT_POSITIVE
17:27:25.288 Revision: 0
17:27:25.288 Actual: 43.5
17:27:25.288 Forecast: 42.4
17:27:25.288 Previous: 36.3
17:27:25.288 ✅ Event #454
17:27:25.288 ID: 274484
17:27:25.288 Event ID: 840140001
17:27:25.288 Time: 2025.12.31 16:30:00
17:27:25.288 Impact: CALENDAR_IMPACT_POSITIVE
17:27:25.288 Revision: 0
17:27:25.288 Actual: 199.0
17:27:25.288 Revised: 215.0
17:27:25.288 Forecast: 227.0
17:27:25.288 Previous: 214.0
17:27:25.301 ✅ Received values BY CURRENCY "EUR": 3116 → 이 중 필터링된 항목: 0
17:27:25.301 ✅ Events from Server Loaded: 다음 60개 이벤트: EUR
17:27:25.301 ✅ Event #0
17:27:25.301 ID: 231322
17:27:25.301 Event ID: 999030013
17:27:25.301 Time: 2025.01.07 13:00:00
17:27:25.301 Impact: CALENDAR_IMPACT_NA
17:27:25.301 Revision: 1
17:27:25.301 Actual: 2.4
17:27:25.301 Forecast: 2.4
17:27:25.301 Previous: 2.2
17:27:25.301 ✅ Event #1
17:27:25.301 ID: 187384
17:27:25.301 Event ID: 999040007
17:27:25.301 Time: 2025.01.08 13:00:00
17:27:25.301 Impact: CALENDAR_IMPACT_POSITIVE
17:27:25.301 Revision: 0
17:27:25.301 Actual: 21.0
17:27:25.301 Revised: 17.8
17:27:25.301 Forecast: 15.7
17:27:25.301 Previous: 17.7
...
17:27:25.303 ✅ Event #58
17:27:25.303 ID: 204746
17:27:25.303 Event ID: 999010006
17:27:25.303 Time: 2025.12.18 16:15:00
17:27:25.303 Impact: CALENDAR_IMPACT_NA
17:27:25.303 Revision: 0
17:27:25.303 Actual: 2.0
17:27:25.303 Previous: 2.0
17:27:25.303 ✅ Event #59
17:27:25.303 ID: 204762
17:27:25.303 Event ID: 999010007
17:27:25.303 Time: 2025.12.18 16:15:00
17:27:25.303 Impact: CALENDAR_IMPACT_NA
17:27:25.303 Revision: 0
17:27:25.303 Actual: 2.15
17:27:25.303 Previous: 2.15
17:27:25.303 ⚠️ Mode: 실시간 (API에서 가져온 데이터)
필터링 및 로깅을 포함한 전체 다운로드에는 약 50ms가 소요됩니다 - 이는 이벤트의 다운로드/필터링 시간을 무시해도 될 만큼 충분히 빠른 속도입니다. 두 로그 파일을 비교해 보면 이벤트 목록의 이벤트 수와 필드 값 모두 완전히 동일하다는 것을 알 수 있습니다. 데이터 변환 체인 유효성 검사가 성공적으로 완료되었습니다.
흔히 발생하는 실수와 잘못된 기대에 대한 분석
MQL5 경제 달력를 통합할 때 개발자들이 가장 흔히 저지르는 6가지 실수를 살펴보겠습니다. 모두 실제 상황에서 테스트 된 것들로 이로 인해 예치금을 날릴 수도 있습니다.
1. " 달력은 움직임을 예측합니다 "
잘못된 기대:
중요도가 매우 높은 뉴스가 발표되고 실제 결과가 예측과 크게 다를 경우, 가격은 반드시 차이가 난 방향으로 움직일 것입니다. 기준 통화의 실제값 > 예상치 이면 매수하십시오.
현실:
경제 달력은 데이터 소스이지 거래 신호 생성기가 아닙니다. 무슨 일이 일어났는지는 알려주지만 시장이 어떻게 반응할지는 알려주지 않습니다. 가격이 "명백한" 논리에 반하여 움직이는 이유는 무엇일까요?
- 시장은 발표 며칠 전부터 기대치를 가격에 반영했습니다. 뉴스가 발표되는 바로 그 순간, "뉴스에 매도"가 이루어집니다.
- 중앙은행이 통화정책을 긴축으로 하는 시기에 높은 인플레이션 수치가 나오면 통화 가치가 상승할 수 있지만 통화정책을 완화하는 시기에는 과열 우려로 인해 매도세가 나타날 수 있습니다.
- 뉴스가 좋은 것 일지라도 이전 수치가 하향 조정되면 전체적인 신호는 모호해집니다.
- 여러 통화에 대한 데이터가 동시에 발표되면 선형적으로 해석할 수 없는 교차 효과가 발생합니다.
올바른 접근 방식은 달력을 진입 트리거가 아닌 변동성 필터로 사용하는 것입니다.
//--- instead of if(actual > forecast) OrderSend(...); //--- use: if(IsHighImpactNewsComingSoon(30)) { //--- reduce the position size or temporarily suspend trading ReduceRiskExposure(); }
2. " HIGH 이벤트는 모두 똑같이 중요합니다."
잘못된 기대:
importance == CALENDAR_IMPORTANCE_HIGH인 경우 해당 이벤트는 시장에 50핍 이상의 변동을 가져올 것이 확실합니다. 우리는 동일한 알고리즘을 사용하여 그러한 모든 뉴스에 매매 할 수 있습니다.
현실:
ENUM_CALENDAR_EVENT_IMPORTANCE 열거형의 특정 값은 달력 편집자의 주관적인 평가이며 장 영향력에 대한 정량적 지표가 아닙니다. 예를 들어 시장에서의 중요도가 서로 다른 두 가지 사건을 'HIGH”로 표기해 보겠습니다.
첫 번째 이벤트인 미국 비농업 고용지표는 주요 고용 지표이며 연준의 정책에 영향을 미치기 때문에 80~150포인트의 변동성을 가져옵니다. 두 번째 지표인 유로존 제조업 구매관리자지수(PMI)는 특정 부문만을 대상으로 하는 지표이며 유럽중앙은행(ECB)에게는 부차적인 지표이기 때문에 10~30포인트 정도의 반응을 가져옵니다.
올바른 접근 방식은 중요도 필터링에 우선순위 이벤트 코드 목록을 추가하는 것입니다:
//--- a list of events that are really worth reacting to bool IsGoodEvent(const string event_code) { static const string tier1_codes[] = { "NONFARM", "CPI", "GDP", "RATE", "FOMC", "ECB_RATE", "RETAIL_SALES", "UNEMPLOYMENT", "PMI_MANUFACTURING" }; for(int i = 0; i < ArraySize(tier1_codes); i++) if(StringFind(event_code, tier1_codes[i]) != -1) return true; return false; } //--- use in filter if(event.importance == CALENDAR_IMPORTANCE_HIGH && IsGoodEvent(event.event_code)) { //--- handle only truly significant news }
추천:
과거의 변동성 분석을 기반으로 자신만의 이벤트 순위를 생성하세요 - 미리 설정된 어떤 순위 지정 방식보다 더 신뢰할 수 있는 것입니다.
3. " TimeTradeServer() 대신 TimeLocal()을 사용했습니다."
잘못된 기대:
MqlCalendarValue :: time 구조체의 이벤트 시간은 제 로컬 시간대(또는 UTC)로 지정되어 있으므로 TimeLocal() 또는 TimeGMT()와 비교할 수 있습니다.
현실:
모든 경제 달력 함수는 거래 서버의 시간대(TimeTradeServer())를 기준으로 시간을 반환합니다. 이는 수동 변환의 필요성을 없애는 아키텍처적 결정이지만 개발자에게는 적응이 필요합니다.
이 오류의 결과는 심각합니다 - 서버가 EET 시간대(UTC+2)에 있고 TimeLocal() 함수(예: MSK, UTC+3)를 사용하는 경우 여러분은 1시간의 시간 차이를 두고 이벤트를 확인하게 되는 것입니다. EA는 뉴스를 놓치거나 반대로 뉴스가 발생한 후에야 반응할 수도 있습니다.
올바른 접근 방식은 모든 시간 비교에는 항상 TimeTradeServer()를 사용하는 것입니다.
//--- WRONG — risk of desynchronization datetime now = TimeLocal(); if(event.time - now < 1800) { ... } //--- RIGHT — guaranteed synchronization datetime now = TimeTradeServer(); if(event.time - now < 1800) { ... } //--- in the tester, TimeTradeServer() returns the model time, so the logic works identically to live
추천:
TimeToString(TimeTradeServer(), TIME_MINUTES)의 출력값을 디버그 로그에 추가하고 이벤트 시간과 비교합니다. 변환 없이 두 값이 일치해야 합니다.
4. " 이벤트 필드가 비어 있을 수 있다는 걸 잊었습니다."
잘못된 기대:
actual_value, forecast_value 및 기타 필드에는 항상 정확한 숫자 값이 포함되어 있습니다. 100만(1,000,000)으로 나누어 비교해 보면 됩니다.
현실:
문서에 따르면 MqlCalendarValue 구조체의 숫자 필드는 값이 지정되지 않은 경우 100만 또는 LONG_MIN 상수를 곱한 값을 저장합니다. 만약 우리가 확인을 무시한다면 어떻게 될까요?
//--- error double actual = values[i].actual_value / 1000000.0; // if actual_value == LONG_MIN, result: -9223372036.854776 if(actual > forecast) // comparison with a "garbage" number causes a false alarm OpenBuy();
올바른 접근 방식은 MqlCalendarValue 구조체의 내장 메서드를 사용하여 값을 안전하게 가져오는 것입니다:
//--- the right approach is built-in methods if(values[i].HasActualValue() && values[i].HasForecastValue()) { double actual = values[i].GetActualValue(); double forecast = values[i].GetForecastValue(); if(!MathIsNaN(actual) && !MathIsNaN(forecast)) { double deviation = actual - forecast; // ... analysis logic } } //+------------------------------------------------------------------+
주의:
GetActualValue(), GetForecastValue() 및 기타 함수는 데이터가 없는 경우 NaN을 반환합니다. 계산에 사용하기 전에 항상 MathIsValidNumber() 또는 MathIsNaN() 함수를 통해 결과가 유효한지 확인해야 합니다.
5. "OnTick()에서 달력 API 함수를 호출했습니다"
잘못된 기대:
항상 최신 데이터를 유지하기 위해 매 틱마다 CalendarValueHistory()를 호출하는 것. 이는 EA가 어떠한 속보도 놓치지 않도록 보장합니다.
현실:
달력 함수는 MetaTrader 5가 연결하는 원격 서버를 통해 작동하며 요청 속도에 엄격한 제한이 있습니다. OnTick() 함수를 호출하면 (초당 수십 번 발생할 수 있음) 문제가 발생합니다.
- Error 5204(ERR_CALENDAR_TOO_MANY_REQUESTS) — 달력 접근이 일시적으로 차단되었습니다.
- Execution delays - 매매 중 네트워크 요청으로 인해 슬리피지가 발생합니다.
- Excessive traffic - 동일한 데이터를 반복적으로 다운로드하는 경우.
//--- global variables MqlCalendarValue calendar_cache[]; bool cache_initialized = false; long last_change_id = 0; //+------------------------------------------------------------------+ int OnInit() { //--- initial history loading (once at startup) datetime from = TimeCurrent() - 7 * 86400; datetime to = TimeCurrent() + 30 * 86400; if(CalendarValueHistory(calendar_cache, from, to, "USD")) { cache_initialized = true; } //--- set a timer for periodic updates (no more than 5-10 minutes) EventSetTimer(300); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnTimer() { if(!cache_initialized) return; MqlCalendarValue updates[]; //--- only request changes since the last update if(CalendarValueLast(last_change_id, updates, "USD") > 0) { if(ArraySize(updates) > 0) { //--- merge new data with the cache MergeUpdates(calendar_cache, updates); last_change_id = updates[ArraySize(updates) - 1].change_id; } } } //+------------------------------------------------------------------+ void OnTick() { //--- work only with local cache - no network delays if(cache_initialized) ProcessNewsSignals(calendar_cache); }
추천:
OnTick() 함수는 로컬 데이터에서만 작동해야 합니다. 네트워크 요청은 OnInit(), OnTimer() 또는 사용자 이벤트에서만 처리됩니다.
6. "새로운 이벤트에 대한 테스트를 위해 Expert 버전을 다시 컴파일하는 것을 잊었습니다."
잘못된 기대:
Files 폴더에 있는 calendar_test_res.bin 리소스 파일을 업데이트했습니다. 이제 테스터는 새 이벤트를 자동으로 볼 수 있습니다. EA를 다시 컴파일할 필요는 없습니다.
현실:
#resource 지시어는 컴파일 시점에 데이터를 .ex5 실행 파일에 포함시킵니다. 외부 .bin 파일의 변경 사항은 동적으로 감지되지 않습니다 - EA는 빌드 시점에 컴파일 된 리소스 버전을 계속 사용합니다.
오류 증상:
- 테스터 로그에는 마지막 컴파일 이후 리소스에 추가된 이벤트가 포함되지 않습니다.
- 해당 전략은 파일에 중요한 뉴스가 있음에도 불구하고 "뉴스를 알아채지 못합니다".
- 코드는 논리적으로는 올바르지만 테스트 결과는 예상과 다릅니다.
리소스를 업데이트한 후에는 항상 EA를 다시 컴파일하는 것이 올바른 접근 방식입니다.
//--- at the beginning of the EA file #resource "\\Files\\USD_calendar_test_res.bin" as MqlCalendarValue USD_res_calendar_data[] //--- after USD_calendar_test_res.bin update: // 1. Save changes to the resource file. // 2. Press F7 in MetaEditor (or Compile in the menu). // 3. Make sure there are no errors in the compilation log. // 4. Run the test again.
추천:
.ex5 파일의 데이터 관련성을 시각적으로 모니터링할 수 있도록 컴파일 로그에 리소스 버전 출력(예: 해시 또는 마지막 업데이트 날짜)을 추가합니다.
비고:
이 섹션에서 논의된 오류는 MQL5 구문에 대한 지식이 부족에서 발생하는 것이 아니라 경제 데이터(지표)의 본질에 대한 단순화된 이해에서 비롯됩니다. 이 여섯 가지 실수를 피하는 방법을 이해하는 사람은 작동하는 코드를 가지게 되는 물론이고 시장의 변화와 규제 기관의 규제에 적응할 수 있는 안정적인 매매 시스템을 얻게 될 것입니다. 바로 이러한 접근 방식이 알고리즘 트레이딩에서 아마추어와 전문가를 구분 짓는 요소입니다.
결론
MetaTrader 5 터미널의 경제 달력은 강력한 도구입니다. 하지만 어떤 도구든 마찬가지이지만 맥락을 이해하고 사용법을 숙지하고 플랫폼의 한계를 이해해야 합니다.
우리는 MetaTrader 5의 EA용 뉴스 API에 대한 구체적인 구현 방법과 사용 구조에 대해 알아보았습니다. 이 글은 여러 가지 실용적인 요소들을 제시하고 이러한 요소들을 종합하면 수동적으로 하던 뉴스 트레이딩을 언제나 사용 가능한 모듈로 전환할 수 있다는 것을 보여줍니다:
- 달력 API 이해 (이벤트와 값의 차이점, MqlCalendarEvent/MqlCalendarValue 구조, 서버 시간);
- 안전한 로딩 및 업데이트 방법(초기 로딩에는 CalendarValueHistory, 증분 업데이트에는 CalendarValueLast + change_id 사용);
- 일반적인 오류를 처리하고 요청 한도를 준수해야 합니다;
- 통화, 중요도, 이벤트 코드 및 시간 창을 기준으로 다단계 필터링을 적용하여 노이즈를 줄이고 3~5개의 중요한 이벤트만 남깁니다;
- 필터링된 데이터를 바이너리 리소스로 내보내고, 데이터 소스를 "실시간" ↔ "전략 테스터"로 자동 전환하는 메커니즘을 통해 실시간과 백테스팅에서 동일한 동작을 보장하도록 합니다.
모듈 준비 기준: 지정된 기간의 이벤트가 성공적으로 로드됩니다; change_id를 통한 증분 업데이트가 제한을 초과하지 않고 작동합니다; 테스터에서 EA는 리소스를 읽고 동일한 입력 데이터를 사용하여 실제 환경과 동일한 결정을 내립니다.
권장되는 이후 단계는 내보내기를 구현하고 .bin 파일을 #resource에 하드코딩하고, OnInit/OnTimer 업데이트 로직을 테스트하고, 통제된 백테스트(예: "30분 동안 거래하지 않고 60분 후에 재개" 시나리오)를 실행하는 것입니다.
이를 통해 가설 단계의 시스템에서 검증 가능하고 확장 가능한 뉴스 트레이딩 시스템으로 나아갈 수 있는 것입니다. 뉴스 실행 엔진(EA)을 개발하는 것은 단순한 프로그래밍이 아닙니다; 거시경제 이론과 시장 관행을 연결하는 다리를 놓는 일입니다.
MetaTrader 5 달력 API 함수 및 뉴스 트레이딩에서 활용하는 법에 대한 심층 학습을 위한 추천 자료:
- 문서 — 경제 달력 함수;
- 기고글 MQL5 경제 달력을 활용한 매매 (1부): MQL5 경제 달력의 함수들 완벽하게 이해하기;
- 기고글 MQL5 경제 달력을 활용한 거래 (7부): 리소스 기반 뉴스 이벤트 분석을 활용한 전략 테스트 준비;
- 기고글 MQL5 경제 달력을 활용한 거래 (8부): 스마트 이벤트 필터링 및 타겟팅 로그를 활용한 뉴스 기반 백테스팅 최적화;
- 기고글 MQL5 쿡북 – 경제 달력.
이 글에 첨부된 파일 목록:
| 파일 이름 | 설명 |
|---|---|
| CalendarEventMonitor-EA.mq5 | 달력 데이터 업데이트 함수를 테스트하기 위한 테스트 스크립트 코드. |
| ExportCalendarForTester-S.mq5 | 지정된 필터를 사용하여 이벤트를 바이너리 파일로 내보내는 기능을 확인하는 테스트 스크립트 코드. |
| GetTodayEvents-S.mq5 | 현재 날짜의 이벤트 검색이 제대로 되는지 확인하는 테스트 스크립트 코드. |
| ImportCalendarValidation-EA.mq5 | 리소스에서 전략 테스터를 통해 뉴스를 검색하는 기능을 확인하기 위한 테스트 EA 코드. |
| ImportTesterLog.txt | 시각적 모드에서 테스터를 사용하여 ImportCalendarValidation-EA.mq5 EA를 한 번 실행한 결과입니다. |
| LoadLiveLog.txt | ImportCalendarValidation-EA.mq5 EA에서 이벤트의 실시간 로딩 결과 |
MetaQuotes 소프트웨어 사를 통해 러시아어가 번역됨.
원본 기고글: https://www.mql5.com/ru/articles/22196
경고: 이 자료들에 대한 모든 권한은 MetaQuotes(MetaQuotes Ltd.)에 있습니다. 이 자료들의 전부 또는 일부에 대한 복제 및 재출력은 금지됩니다.
새로운 기능: MQL5의 커스텀 인디케이터
파이썬 + MetaTrader 5: 데이터, 피처 및 프로토타입을 위한 고속의 연구용 프레임워크
새 MetaTrader 와 MQL5를 소개해드립니다
루틴 없는 알고리즘 트레이딩: MetaTrader 5에서 SQLite를 사용하여 트레이딩 분석을 빠르게 하는 방법
작성 내용:
MQL5 API에 변경 사항이 있다면 지적해 주시기 바랍니다만, 예전에는 캘린더가 일광 절약 시간제(DST)를 고려하여 '현재' 시간대에 맞춰 조정되었습니다. 즉, 여름철에는 함수 호출 시 UTC+3 시간대의 타임스탬프가 반환되는 반면, 반면 겨울철에 동일한 기간의 기록에 대한 요청은 UTC+2 시간대의 타임스탬프가 붙은 이벤트를 반환받게 됩니다. 이는 서버가 일광 절약 시간제(DST)로 전환하고 다시 원래 시간대로 되돌리는 경우입니다. 따라서 반년 이상의 기간에 걸친 캘린더 기록을 정확하게 불러오려면 브로커의 시간대 변경 이력을 분석해야 합니다. 자세한 내용은 코드베이스에서 확인하세요.
또한 리소스 형태의 캘린더 링크 캐시 방식은 실용성이 떨어질 뿐만 아니라, 특히 본문에서 언급된 오류(예: 전문가(Expert)를 재컴파일하는 것을 잊지 마세요)를 유발합니다. 모든 에이전트가 접근할 수 있는 Common 폴더에 있는 파일로 캘린더 캐시를 입력 매개변수에 지정하는 것은 어떨까요?
그런데 왜 호가(사실상 모든 타임스탬프)가 예를 들어 UTC로 관리되지 않는 것일까요?
왜 딜러들의 틱이 동일한 시간 기준에 대해 서로 다른 수치로 표현되는 것일까요?
철학적 질문이네요 :-) 그냥 관례가 그렇게 되어버렸을 뿐입니다. 비록 이것이 틀렸고 별다른 이유 없이 문제를 일으키긴 하지만요.
추신. 그래서 “과거 뉴스”는 다른 출처에서 가져오는 편이 좋습니다.
그리고 절대로 직접 “시계 변환 이력을 분석”해서는 안 됩니다. 이 모든 것은 이미 존재하며, OS 기능이나 시스템 라이브러리의 일부입니다. tzdata를 검색해 보세요
자전거를 얼마나 더 만들 수 있을까, 게다가 엉성한 자전거를
이전에는 달력이 일광 절약 시간제(DST)를 고려하여 현재 시간대에 맞춰 조정되었는데, 즉 여름철에는 함수 호출 시 UTC+3 시간대의 타임스탬프가 반환되고, 반면 겨울철에 동일한 기간의 기록을 조회하면, 서버가 일광 절약 시간제(DST)로 전환하고 다시 원래 시간대로 되돌리는 경우, UTC+2 시간대의 타임스탬프가 붙은 이벤트를 받게 됩니다.
추신. 그러니 "역사 관련 뉴스"는 다른 출처에서 확인하는 편이 좋습니다.
그리고 절대 직접 "시계 번역의 역사"를 "분석"해서는 안 됩니다. 이 모든 정보는 이미 존재하며, OS의 기능이나 시스템 라이브러리의 일부입니다. tzdata를 구글에서 검색해 보세요.
자전거를 얼마나 더 만들 수 있을까, 게다가 엉성한 자전거를
다른 출처로는 문제를 해결할 수 없습니다. 이 문제는 MT5에서 시세 데이터를 저장하는 방식에 깊이 뿌리박혀 있기 때문입니다.
우선 여러분이 만든 제대로 된 자전거를 보여주신 다음에 조언을 해 주세요.
현대 뉴스 트레이더가 직면한 주요 문제는 도구 세트가 파편화되어 있고 체계적인 알고리즘 기반 거래 워크플로가 부족하다는 점입니다. 거래를 진행하는 동안 인터넷 브라우저(뉴스 사이트 탐색)와 거래 터미널 사이에서 주의를 분산시키는 것은 상당히 어렵습니다.
뉴스 트레이더의 워크플로는 다음과 같습니다. 웹 브라우저에서 뉴스 캘린더를 빠르게 열어 이벤트 변경 사항을 확인 → 예정된 이벤트를 신속하게 평가하고 무엇을, 어떻게 거래할지 결정 → MetaTrader 5 터미널로 이동하여 보류 주문을 넣거나, 터미널 앞에 앉아 뉴스 발표를 기다리며 결정을 내립니다. 이러한 상황에서 트레이더는 종종 맥락을 놓치게 되며, 이는 뉴스에 대한 반응 지연으로 이어지고, 결국 손실로 이어집니다.
뉴스 트레이딩을 명확한 규칙, 재현 가능한 결과, 자동화된 테스트가 적용된 공학적 문제처럼 처리하고 싶으신가요? 이 기사의 목적은 MetaTrader 5용 뉴스 레이어의 작동 아키텍처를 보여주는 것입니다: 단일 데이터 소스, 캘린더 API의 적절한 활용, 필터링 및 캐싱 메커니즘, 테스터용 리소스로의 과거 이벤트 내보내기, 그리고 라이브와 테스터 간의 자동 전환을 통해 동일한 코드가 실시간 데이터와 과거 데이터 모두에서 결정론적인 결과를 산출하도록 하는 것입니다.
네, 이것이 바로 뉴스 트레이딩의 진정한 고충입니다.
브라우저, 캘린더, 터미널 사이를 오가다 보면 집중력이 흐트러지고 반응 속도도 크게 떨어집니다. 뉴스를 거래 환경으로 직접 불러오는 통합된 워크플로우나 시스템을 갖추면 실행 속도가 확실히 빨라지고 일관성도 높아질 것입니다.
테스트와 자동화를 통해 이를 구조화되고 반복 가능한 “엔지니어링 방식”의 설정으로 전환한다는 아이디어는, 감정적 결정이나 지연된 결정을 피하는 데 실제로 매우 합리적입니다.