
Упрощаем торговлю на новостях (Часть 6): Совершаем сделки (III)
Введение
В этой статье мы внесем улучшения в базу данных хранилища, будут добавлены новые представления данных, такие как отображение дат последних новостей или следующей новости для каждого уникального события в экономическом календаре MQL5. Это улучшит опыт пользователя при использовании программы, поскольку позволит ему быть в курсе будущих или прошедших событий. Кроме того, меню ввода советника будет расширено для включения сортировки новостей и методов ввода стоп-ордеров.
Также код советника будет обновлен для использования предыдущего кода, написанного для сокращения времени выполнения советника в тестере стратегий, из статьи "Упрощаем торговлю на новостях (Часть 4): Повышаем производительность", а также кода из статьи "Упрощаем торговлю на новостях (Часть 5): Совершаем сделки (II)", где мы управляем проскальзыванием и открываем стоп-ордера.
Настройки новостей
- SELECT NEWS OPTION - выбрать новостной профиль. Доступные профили:
- NEWS SETTINGS - сортировать новости по:
- CALENDAR IMPORTANCE - важность календаря
- EVENT FREQUENCY - частота события
- EVENT SECTOR - сектор, к которому относится событие
- EVENT TYPE - тип события
- EVENT CURRENCY - валюта события
- CUSTOM NEWS EVENTS - сортировать новости в зависимости от идентификаторов событий, введенных в качестве входных данных (до 14 идентификаторов событий на параметр).
Настройки торговли
- SELECT TRADE ENTRY OPTION - разрешить разные методы входа в торговлю. Доступные методы:
- MARKET POSITION - только рыночное исполнение (покупка и продажа). Мы должны заранее знать направление торговли по выбранной новости (покупка или продажа).
- STOP ORDERS - использовать стоп-ордера на покупку и продажу до выхода выбранной новости. Нужно задать отклонение цены, чтобы у советника был ценовой буфер для размещения стоп-ордеров. После срабатывания стоп-ордера противоположный будет удален. Например, когда стоп-ордера на покупку и продажу размещаются перед NFP (данные о занятости в несельскохозяйственном секторе) и стоп-ордер на покупку срабатывает (покупка при достижении определенной цены), советник удалит оставшийся стоп-ордер на продажу. Этот метод входа в торговлю не требует воздействия события для предварительного размещения сделки.
- SINGLE STOP ORDER - использовать один стоп-ордера - на покупку или продажу. Два основных требования:
- Мы должны заранее знать направление торговли по выбранной новости.
- Нужно задать отклонение цены, чтобы у советника был ценовой буфер для размещения стоп-ордера.
Класс News
В заголовочном файле News.mqh мы объявим перечисление NewsSelection вне класса CNews. Цель перечисления - позволить пользователям выбирать различные профили новостей в пределах входных данных советника. У нас также будет переменная myNewsSelection, которая будет хранить предпочтительный выбор пользователя. Кроме того, мы объявим структуру под названием CustomEvent. Эта структура будет хранить логическое значение, которое определяет, следует ли сортировать идентификаторы событий в строковом массиве EventIds внутри структуры. Кроме того, в структуре объявлены переменные, такие как CEvent1. Эта переменная будет действовать как один из пяти вариантов, с помощью которых пользователь/трейдер может сортировать идентификаторы пользовательских событий.
Перечисления:
- NewsSelection определяет два профиля:
- News_Select_Custom_Events - для пользовательских новостей.
- News_Select_Settings - для настроек новостей.
- myNewsSelection - переменная типа перечисления, которая хранит текущий выбор профиля новостей.
Структуры:
- CustomEvent - структура для хранения идентификаторов пользовательских событий и флаг (useEvents), указывающий, следует ли включать эти события в запрос.
- Существуют пять переменных: CEvent1, CEvent2, CEvent3, CEvent4 и CEvent5 типа CustomEvent, каждая из которых представляет отдельную группу событий.
//--- Enumeration for News Profiles enum NewsSelection { News_Select_Custom_Events,//CUSTOM NEWS EVENTS News_Select_Settings//NEWS SETTINGS } myNewsSelection; //--- Structure to store event ids and whether to use these ids struct CustomEvent { bool useEvents; string EventIds[]; } CEvent1,CEvent2,CEvent3,CEvent4,CEvent5;
Перечисление CalendarComponents:
- CalendarComponents - перечисляет различные компоненты экономического календаря, такие как таблицы и представления, используемые для структурирования данных, связанных с переходом на летнее время (DST), информацией о событиях и данными о валютах.
В перечисление CalendarComponents мы добавили два новых значения:
- RecentEventInfo_View
- UpcomingEventInfo_View
//-- To keep track of what is in our database enum CalendarComponents { // ... RecentEventInfo_View,//View for Recent Dates For Events UpcomingEventInfo_View,//View for Upcoming Dates For Events // ... };
Функция GetCalendar(CalendarData &Data[]):
- Функция извлекает все соответствующие данные календаря из базы данных календаря в хранилище и сохраняет их в массиве данных.
- Она открывает базу данных (NEWS_DATABASE_FILE) и выполняет SQL-запрос на основе текущей выборки новостей (myNewsSelection).
- В зависимости от того, выбрано ли News_Select_Custom_Events или News_Select_Settings, генерируется разный SQL-запрос для извлечения информации о событиях.
- Custom Events - объединяет таблицы MQL5Calendar и TimeSchedule для извлечения пользовательских новостей с использованием фильтра на основе идентификаторов пользовательских событий.
- News Settings - извлекает данные о событиях, отсортированные по указанным пользователем настройкам, таким как важность, частота, сектор, тип и валюта.
- Функция обрабатывает результаты SQL-запроса и сохраняет полученные данные в массиве Data.
- Если запрос к базе данных не выполнен, выводится ошибка и невыполненный SQL-запрос.
//--- Will Retrieve all relevant Calendar data for DB in Memory from DB in Storage void GetCalendar(CalendarData &Data[]) { // ... string SqlRequest; //--- switch statement for different News Profiles switch(myNewsSelection) { case News_Select_Custom_Events://CUSTOM NEWS EVENTS //--- Get filtered calendar DB data SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency," "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency," "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ " "Inner Join %s TS on TS.ID=MQ.ID Where %s OR %s OR %s OR %s OR %s;", CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name, Request_Events(CEvent1),Request_Events(CEvent2),Request_Events(CEvent3), Request_Events(CEvent4),Request_Events(CEvent5)); break; case News_Select_Settings://NEWS SETTINGS //--- Get filtered calendar DB data SqlRequest = StringFormat("Select MQ.EventId,MQ.Country,MQ.EventName,MQ.EventType,MQ.EventImportance,MQ.EventCurrency," "MQ.EventCode,MQ.EventSector,MQ.EventForecast,MQ.EventPreValue,MQ.EventImpact,MQ.EventFrequency," "TS.DST_UK,TS.DST_US,TS.DST_AU,TS.DST_NONE from %s MQ " "Inner Join %s TS on TS.ID=MQ.ID " "Where %s and %s and %s and %s and %s;", CalendarStruct(MQL5Calendar_Table).name,CalendarStruct(TimeSchedule_Table).name, Request_Importance(myImportance),Request_Frequency(myFrequency), Request_Sector(mySector),Request_Type(myType),Request_Currency(myCurrency)); break; default://Unknown break; } // ...
Функция Request_Events(CustomEvent &CEvent):
- Функция генерирует предложение SQL WHERE для запроса к базе данных на основе идентификаторов пользовательских событий, хранящихся в структуре CustomEvent.
- Она проверяет, является ли useEvents истинным. Если это так, она добавляет каждый идентификатор события из массива CEvent.EventIds[] в SQL-запрос.
//--- Retrieve Sql request string for custom event ids string Request_Events(CustomEvent &CEvent) { //--- Default request string string EventReq="MQ.EventId='0'"; //--- Check if this Custom event should be included in the SQL request if(CEvent.useEvents) { //--- Get request for first event id EventReq=StringFormat("(MQ.EventId='%s'", (CEvent.EventIds.Size()>0)? CEvent.EventIds[0]:"0"); //--- Iterate through remaining event ids and add to the SQL request for(uint i=1;i<CEvent.EventIds.Size();i++) { EventReq+=StringFormat(" OR MQ.EventId='%s'",CEvent.EventIds[i]); } EventReq+=")"; } //--- Return SQL request for custom event ids return EventReq; }
Члены открытого класса:
Обновления вносятся в следующие функции: EconomicDetailsMemory,EconomicNextEvent и isEvent.
- EconomicDetailsMemory - извлекает значения из базы данных календаря в памяти.
- EconomicNextEvent - обновляет структурную переменную данными следующего события.
- isEvent - убеждается, что новостное событие вот-вот произойдет и соответствующим образом изменяет переданные параметры.
//Public declarations accessable via a class's Object public: // ... void EconomicDetailsMemory(Calendar &NewsTime[],datetime date,bool ImpactRequired);//Gets values from the MQL5 DB Calendar in Memory void EconomicNextEvent();//Will update UpcomingNews structure variable with the next event data // ... //--- Checks if a news event is occurring and modifies the parameters passed by reference bool isEvent(uint SecondsPreEvent,string &Name,string &Importance,string &Code);
Целью функции EconomicDetailsMemory является извлечение данных о событиях экономического календаря из базы данных в памяти (DBMemory) на определенную дату, при необходимости учитывая влияние события, и сохранение данных в массиве NewsTime[].
- Функция извлекает из базы данных сведения об экономических событиях за указанную дату и сохраняет их в массиве NewsTime[].
- Если ImpactRequired равен true, он извлекает предыдущие и прогнозируемые значения события и назначает уровень воздействия (impact) на основе исторических данных.
- Если ImpactRequired равен false, он просто извлекает данные текущего дня и следующего события.
- Результаты извлекаются с помощью подготовленного SQL-запроса и сохраняются в массиве NewsTime[], размер которого динамически изменяется в зависимости от количества извлеченных событий.
//+------------------------------------------------------------------+ //|Gets values from the MQL5 DB Calendar in Memory | //+------------------------------------------------------------------+ void CNews::EconomicDetailsMemory(Calendar &NewsTime[],datetime date,bool ImpactRequired) { //--- SQL query to retrieve news data for a certain date string request_text; //--- Check if Event impact is required for retrieving news events if(ImpactRequired) { request_text=StringFormat("WITH DAILY_EVENTS AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as" " 'Type',M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE" " as 'Code',M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE'," "M.EVENTFREQUENCY as 'Freq' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))=DATE(REPLACE('%s','.','-'))" " AND (Forecast<>'None' AND Prevalue<>'None')),DAILY_IMPACT AS(SELECT DE.E_ID,DE.COUNTRY,DE.Name," "DE.Type,DE.Importance,DE.Time,DE.Currency,DE.Code,DE.Sector,DE.Forecast,DE.Prevalue,DE.Freq," "MC.EVENTIMPACT as 'IMPACT', RANK() OVER(PARTITION BY DE.E_ID,DE.Time ORDER BY MC.%s DESC)DateOrder" " FROM %s MC INNER JOIN DAILY_EVENTS DE on DE.E_ID=MC.EVENTID WHERE DATE(REPLACE(MC.%s,'.','-'))<" "DATE(REPLACE(DE.Time,'.','-')) AND DATE(REPLACE(MC.%s,'.','-'))>=DATE(REPLACE(DE.Time,'.','-')," "'-24 months') AND (MC.EVENTFORECAST<>'None' AND MC.EVENTPREVALUE<>'None' AND (CASE WHEN Forecast>" "Prevalue THEN 'more' WHEN Forecast<Prevalue THEN 'less' ELSE 'equal' END)=(CASE WHEN MC.EVENTFORECAST" ">MC.EVENTPREVALUE THEN 'more' WHEN MC.EVENTFORECAST<MC.EVENTPREVALUE THEN 'less' ELSE 'equal' END)) " "ORDER BY MC.%s),DAILY_EVENTS_RECORDS AS(SELECT * FROM DAILY_IMPACT WHERE DateOrder=1 ORDER BY Time" " ASC),NEXT_EVENT AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as 'Type'," "M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE as 'Code'," "M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE',M.EVENTFREQUENCY" " as 'Freq' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))>DATE(REPLACE('%s','.','-')) AND (Forecast<>" "'None' AND Prevalue<>'None' AND DATE(REPLACE(Time,'.','-'))<=DATE(REPLACE('%s','.','-'),'+60 days')))," "NEXT_IMPACT AS(SELECT NE.E_ID,NE.COUNTRY,NE.Name,NE.Type,NE.Importance,NE.Time,NE.Currency,NE.Code" ",NE.Sector,NE.Forecast,NE.Prevalue,NE.Freq,MC.EVENTIMPACT as 'IMPACT',RANK() OVER(PARTITION BY " "NE.E_ID,NE.Time ORDER BY MC.%s DESC)DateOrder FROM %s MC INNER JOIN NEXT_EVENT NE on NE.E_ID=MC.EVENTID " "WHERE DATE(REPLACE(MC.%s,'.','-'))<DATE(REPLACE(NE.Time,'.','-')) AND DATE(REPLACE(MC.%s,'.','-'))>=" "DATE(REPLACE(NE.Time,'.','-'),'-24 months') AND (MC.EVENTFORECAST<>'None' AND MC.EVENTPREVALUE<>'None'" " AND (CASE WHEN Forecast>Prevalue THEN 'more' WHEN Forecast<Prevalue THEN 'less' ELSE 'equal' END)=" "(CASE WHEN MC.EVENTFORECAST>MC.EVENTPREVALUE THEN 'more' WHEN MC.EVENTFORECAST<MC.EVENTPREVALUE THEN " "'less' ELSE 'equal' END)) ORDER BY MC.%s),NEXT_EVENT_RECORD AS(SELECT * FROM NEXT_IMPACT WHERE " "DateOrder=1 ORDER BY Time ASC LIMIT 1),ALL_EVENTS AS(SELECT * FROM NEXT_EVENT_RECORD UNION ALL " "SELECT * FROM DAILY_EVENTS_RECORDS)SELECT E_ID,Country,Name,Type,Importance,Time,Currency,Code," "Sector,Forecast,Prevalue,Impact,Freq FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;", EnumToString(MySchedule),DBMemory.name,TimeToString(date),EnumToString(MySchedule),DBMemory.name, EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule) ,DBMemory.name,TimeToString(date),TimeToString(date),EnumToString(MySchedule),DBMemory.name, EnumToString(MySchedule),EnumToString(MySchedule),EnumToString(MySchedule)); } else { /* Within this request we select all the news events that will occur or have occurred in the current day and the next news event after the current day */ request_text=StringFormat("WITH DAILY_EVENTS AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as" " 'Type',M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE" " as 'Code',M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE'" ",M.EVENTFREQUENCY as 'Freq',M.EVENTIMPACT as 'Impact' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))" "=DATE(REPLACE('%s','.','-'))),DAILY_EVENTS_RECORDS AS(SELECT * FROM DAILY_EVENTS ORDER BY Time ASC)" ",NEXT_EVENT AS(SELECT M.EVENTID as 'E_ID',M.COUNTRY,M.EVENTNAME as 'Name',M.EVENTTYPE as 'Type'," "M.EVENTIMPORTANCE as 'Importance',M.%s as 'Time',M.EVENTCURRENCY as 'Currency',M.EVENTCODE as " "'Code',M.EVENTSECTOR as 'Sector',M.EVENTFORECAST as 'Forecast',M.EVENTPREVALUE as 'PREVALUE'," "M.EVENTFREQUENCY as 'Freq',M.EVENTIMPACT as 'Impact' FROM %s M WHERE DATE(REPLACE(Time,'.','-'))" ">DATE(REPLACE('%s','.','-')) AND (DATE(REPLACE(Time,'.','-'))<=DATE(REPLACE('%s','.','-')," "'+60 days'))),NEXT_EVENT_RECORD AS(SELECT * FROM NEXT_EVENT ORDER BY Time ASC LIMIT 1)," "ALL_EVENTS AS(SELECT * FROM NEXT_EVENT_RECORD UNION ALL SELECT * FROM " "DAILY_EVENTS_RECORDS)SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;", EnumToString(MySchedule),DBMemory.name,TimeToString(date),EnumToString(MySchedule),DBMemory.name, TimeToString(date),TimeToString(date)); } // ...
Сигнатура функции
- Аргументы:
- Calendar &NewsTime[] - массив структур календаря (каждая из которых содержит сведения о событиях), которая будет заполнена извлеченными данными.
- datetime date - конкретная дата, для которой извлекаются экономические события.
- bool ImpactRequired - флаг, указывающий, следует ли учитывать влияние события в запросе.
Построение SQL-запроса на основе требований к воздействию (impact)
SQL-запрос различается в зависимости от того, установлено ли для параметра ImpactRequired значение true или false. При true для направления торговли требуется влияние события, а при false - не требуется, так как направление события не является необходимым для открытия сделки.
А. Если ImpactRequired равен true
Этот запрос более сложный и состоит из нескольких частей:
- Запрос извлекает экономические события на указанную дату, а также учитывает следующее предстоящее событие после текущего дня.
- Он ищет предыдущие события за последние 24 месяца, для которых доступны как прогноз, так и предварительное значение, и сравнивает их с текущим событием.
- Если прогнозируемое значение больше предварительного (или наоборот) или даже равно ему, оно сопоставляется с историческим событием, имеющим ту же тенденцию (прогноз > предварительное значение или прогноз < предварительное значение или прогноз = предварительное значение).
- Влияние прошлого события присваивается текущему.
Вот схема построения запроса:
"WITH DAILY_EVENTS AS(...) , DAILY_IMPACT AS(...) , NEXT_EVENT AS(...), NEXT_IMPACT AS(...),
ALL_EVENTS AS(...) SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;"
- DAILY_EVENTS выбирает все экономические события на указанную дату из календаря. Выбираются события с действительными прогнозным и предварительным значениями.
- DAILY_IMPACT - поиск исторических событий (за последние 24 месяца), где тенденция прогнозного и предварительного значений (больше/меньше или равна) аналогична текущему событию. События ранжируются по дате, чтобы найти самое последнее совпадающее событие, и его влияние присваивается текущему событию.
- NEXT_EVENT - выбирает следующее предстоящее событие после текущего дня.
- NEXT_IMPACT - подобно DAILY_IMPACT, извлекает самое последнее прошедшее событие, которое соответствует тенденции прогноза/предварительного значения, и назначает его влияние следующему событию.
- ALL_EVENTS - объединяет DAILY_EVENTS_RECORDS (для текущего дня) и NEXT_EVENT_RECORD (для следующего события) и упорядочивает их по времени.
B. Если ImpactRequired равен false
Если воздействие не требуется, запрос упрощается:
- Он извлекает все события за текущий день и следующее событие после текущего дня (в течение 60 дней).
Схема SQL-запроса:
"WITH DAILY_EVENTS AS(...) , NEXT_EVENT AS(...), ALL_EVENTS AS(...) SELECT * FROM ALL_EVENTS GROUP BY Time ORDER BY Time Asc;"
- DAILY_EVENTS - извлекает экономические события за текущий день.
- NEXT_EVENT - извлекает следующее предстоящее событие после текущего дня в течение следующих 60 дней.
- ALL_EVENTS - объединяет ежедневные события и следующее событие и упорядочивает их по времени.
Выполнение SQL-запроса
int request = DatabasePrepare(DBMemoryConnection, request_text);
- DatabasePrepare - подготавливает SQL-запрос к выполнению. Сохраненный результат является хэндлом запроса. Этот хэндл будет использоваться для извлечения данных.
- Error Handling - если запрос не выполнен (request == INVALID_HANDLE), выводится сообщение об ошибке и SQL-запрос для отладки.
Чтение результатов
Calendar ReadDB_Data; ArrayRemove(NewsTime, 0, WHOLE_ARRAY); for (int i = 0; DatabaseReadBind(request, ReadDB_Data); i++) { ArrayResize(NewsTime, i + 1, i + 2); NewsTime[i] = ReadDB_Data; }
- ArrayRemove - очищает массив NewsTime[], чтобы подготовить его для новых данных.
- DatabaseReadBind - извлекает результаты из подготовленного запроса, привязывая каждую строку результата к переменной ReadDB_Data, которая представляет собой структуру календаря.
- ArrayResize - изменяет размер массива NewsTime[] для размещения новых данных. Для каждой строки, возвращаемой DatabaseReadBind, массив увеличивается на 1 элемент.
- NewsTime[i] = ReadDB_Data - копирует извлеченные данные в массив NewsTime[].
Завершение запроса
DatabaseFinalize(request);
- DatabaseFinalize - очищает ресурсы, выделенные DatabasePrepare, освобождая хэндл запроса.
Функция EconomicNextEvent отвечает за определение следующей предстоящей экономической новости и обновление переменной UpcomingNews с указанием его подробностей.
- Функция просматривает все события в CalendarArray и находит то, которое является следующим предстоящим событием, основываясь на времени сервера (TimeTradeServer()).
- Она обновляет структуру UpcomingNews, добавляя сведения о следующем событии.
- Логика гарантирует, что учитываются только будущие события (относительно времени сервера), и выбирается ближайшее предстоящее событие.
//+------------------------------------------------------------------+ //|Will update UpcomingNews structure variable with the next news | //|event data | //+------------------------------------------------------------------+ void CNews::EconomicNextEvent() { //--- Declare unassigned Calendar structure variable Next Calendar Next; //--- assign empty values to Calendar structure variable UpcomingNews UpcomingNews = Next; //--- assign default date datetime NextEvent=0; //--- Iterate through CalendarArray to retrieve news events for(uint i=0;i<CalendarArray.Size();i++) { //--- Check for next earliest news event from CalendarArray if((NextEvent==0)||(TimeTradeServer()<datetime(CalendarArray[i].EventDate) &&NextEvent>datetime(CalendarArray[i].EventDate))||(NextEvent<TimeTradeServer())) { //--- assign values from the CalendarArray NextEvent = datetime(CalendarArray[i].EventDate); Next = CalendarArray[i]; } } //--- assign the next news event data into UpcomingNews variable UpcomingNews = Next; }
Объявление неназначенной структуры календаря
Calendar Next;
- Переменная Next объявлена как структура Calendar (пользовательская структура, содержащая сведения о событии, такие как дата события, название, страна и т. д.).
- Изначально она не назначена и позднее будет содержать данные о следующем экономическом событии.
Присвоение пустых значений UpcomingNews
UpcomingNews = Next;
- UpcomingNews — это глобальная переменная или переменная уровня класса (еще одна структура календаря), которая хранит сведения о следующем предстоящем событии.
- В начале она сбрасывается до значений по умолчанию (пустых) переменной Next.
Присвоение даты по умолчанию
datetime NextEvent = 0;
- Переменная NextEvent инициализируется значением 0, что означает, что событие еще не назначено.
- NextEvent сохранит временную метку (в формате datetime) следующего экономического события в течение цикла.
Проход по CalendarArray
for (uint i = 0; i < CalendarArray.Size(); i++)
- Массив CalendarArray содержит подробную информацию об экономических событиях.
- Цикл for проходит по каждому элементу этого массива (каждый из которых представляет собой событие), проверяя, является ли событие следующим предстоящим событием.
Проверка условий следующего события
if ((NextEvent == 0) || (TimeTradeServer() < datetime(CalendarArray[i].EventDate) && NextEvent > datetime(CalendarArray[i].EventDate)) || (NextEvent < TimeTradeServer()))
Оператор if проверяет несколько условий, чтобы определить, является ли текущее событие в массиве (CalendarArray[i]) следующим новостным событием:
- Первое условие: (NextEvent == 0)
- Если NextEvent по-прежнему равен 0 (событие еще не назначено), в качестве следующего события будет выбрано текущее событие.
- Второе условие: (TimeTradeServer() < datetime(CalendarArray[i].EventDate) && NextEvent > datetime(CalendarArray[i].EventDate))
- Таким образом мы проверяем, произошло ли текущее событие в массиве (CalendarArray[i]) после текущего времени сервера (TimeTradeServer()) и раньше события, которое в данный момент хранится в NextEvent. При true текущее событие становится следующим.
- Третье условие: (NextEvent < TimeTradeServer())
- Если событие, хранящееся в NextEvent, уже произошло (находится в прошлом), функция продолжает поиск допустимого будущего события.
Присвоение значений из CalendarArray
NextEvent = datetime(CalendarArray[i].EventDate);
Next = CalendarArray[i];
- Если хотя бы одно из условий выполнено, текущее событие в CalendarArray[i] определяется как следующее событие:
- NextEvent обновляется на дату текущего события.
- Next обновляется для включения всех сведений (даты, названия, типа и т. д.) о текущем событии.
Назначение следующего события UpcomingNews
UpcomingNews = Next;
- После того, как цикл завершает итерацию по CalendarArray, переменная UpcomingNews обновляется сведениями о следующем предстоящем событии (хранящимся в Next).
- Функция гарантирует, что первое найденное будущее событие относительно текущего времени сервера будет сохранено в UpcomingNews.
Функция isEvent проверяет, произойдет ли новостное событие в ближайшее время или происходит в данный момент в указанном временном диапазоне. Цель этой функции — проверить, происходит ли в настоящее время или должно произойти какое-либо новостное событие из массива CalendarArray, основываясь на смещении по времени. Если такое событие найдено, предоставляются подробные сведения о событии, такие как его название, важность и код.
- Функция просматривает CalendarArray (который содержит данные об экономических событиях) и проверяет, происходит ли какое-либо событие или должно произойти в течение временного диапазона, определенного SecondsPreEvent.
- Если такое событие найдено, она обновляет имя, важность и код с учетом сведений о событии и возвращает значение true.
- Если в указанном диапазоне времени не обнаружено ни одного события, возвращается значение false, а имя, важность и код остаются неизменными (или устанавливаются в значение по умолчанию/NULL).
//+------------------------------------------------------------------+ //|Checks if News is event is about to occur or is occurring | //+------------------------------------------------------------------+ bool CNews::isEvent(uint SecondsPreEvent,string &Name,string &Importance,string &Code) { //--- assign default value Name=NULL; //--- Iterate through CalendarArray for(uint i=0;i<CalendarArray.Size();i++) { //--- Check if news event is within a timespan if(CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(CalendarArray[i].EventDate),SecondsPreEvent), CTime.TimePlusOffset(datetime(CalendarArray[i].EventDate),59))) { //--- assign appropriate CalendarArray values Name=CalendarArray[i].EventName; Importance=CalendarArray[i].EventImportance; Code=CalendarArray[i].EventCode; //--- news event is currently within the timespan return true; } } //--- no news event is within the current timespan. return false; }
Параметры:
- uint SecondsPreEvent - количество секунд до события, в течение которого событие считается "предстоящим".
- string &Name - ссылка на строку, в которой будет храниться имя события, если оно находится в пределах определенного временного диапазона.
- string &Importance - ссылка на строку, в которой будет сохранен уровень важности события, если оно найдено.
- string &Code - ссылка на строку, в которой будет храниться код события.
Присвоение имени значения по умолчанию
Name = NULL;
- Переменная Name инициализируется значением NULL. Если в указанном временном диапазоне не обнаружено ни одного события, Name останется равной NULL.
- Это гарантирует, что Name будет обновлена только в том случае, если в течение указанного периода времени будет обнаружено событие.
Проход по CalendarArray
for (uint i = 0; i < CalendarArray.Size(); i++)
- Цикл проходит по каждому элементу CalendarArray, где каждый элемент представляет собой новостное событие.
- Цикл будет проверять каждое событие, чтобы определить, попадает ли оно в указанный временной диапазон вокруг текущего времени.
Проверим, находится ли событие в указанном временном диапазоне.
if (CTime.TimeIsInRange(CTime.TimeMinusOffset(datetime(CalendarArray[i].EventDate), SecondsPreEvent), CTime.TimePlusOffset(datetime(CalendarArray[i].EventDate), 59)))
- CTime.TimeMinusOffset(datetime(CalendarArray[i].EventDate), SecondsPreEvent):
- Этот вызов функции проверяет, находится ли дата события (CalendarArray[i].EventDate) за вычетом значения SecondsPreEvent в прошлом.
- По сути, он определяет нижнюю границу временного окна (сколько секунд до события).
- CTime.TimePlusOffset(datetime(CalendarArray[i].EventDate), 59):
- Функция проверяет, находится ли дата события на 59 секунд в будущем, определяя верхнюю границу временного окна (как долго событие считается активным).
- Функция CTime.TimeIsInRange проверяет, попадает ли текущее время в этот временной диапазон. Если текущее время находится в этом диапазоне, это означает, что событие должно произойти или уже происходит.
Присвоим значения из CalendarArray
Name = CalendarArray[i].EventName; Importance = CalendarArray[i].EventImportance; Code = CalendarArray[i].EventCode;
- Если событие попадает в указанный временной диапазон, соответствующие данные (название события, важность и код) извлекаются из CalendarArray и присваиваются ссылочным параметрам:
- Name - имя новостного события.
- Importance - уровень важности новостного события.
- Code - код события.
Возвращает true, если событие найдено.
return true;
- Если в указанном временном диапазоне обнаружено новостное событие, функция возвращает true, что указывает на то, что соответствующее событие происходит в данный момент или должно произойти.
Возвращает false, если событие не найдено.
return false;
- Если цикл завершает перебор всех событий в CalendarArray и в указанном диапазоне времени не найдено ни одного события, функция возвращает false, что указывает на отсутствие соответствующего события.
Constructor CNews::CNews(void):
- Инициализирует класс путем настройки операторов SQL DROP, CREATE и INSERT для различных компонентов экономического календаря.
- Tables - определяет такие таблицы, как AutoDST, Record, TimeSchedule и MQL5Calendar.
- Views - определяет виды для различных расписаний летнего времени (Calendar_AU, Calendar_UK, etc.), информации о событиях, валютах и датах недавних/предстоящих событий.
- Triggers - определяет триггеры, такие как OnlyOne_AutoDST и OnlyOne_Record, чтобы гарантировать наличие только одной записи в определенных таблицах.
Каждый компонент (таблица, представление или триггер) инициализируется соответствующими командами SQL, включая создание, вставку и удаление записей.
Создание представления SQL:
- Для каждого компонента расписания и календаря летнего времени определены конкретные представления SQL для структурирования данных в соответствии с различными критериями (например, по важности события, валюте или дате события). Например:
- View for Upcoming Events - отображает даты предстоящих событий, а также дни недели и подробности событий.
- View for Recent Events: аналогично представлению выше, но извлекает даты недавних событий.
Триггеры SQL:
- Триггеры используются для того, чтобы в таблицах AutoDST и Record в любой момент времени существовала только одна запись, путем удаления существующих записей перед вставкой.
//+------------------------------------------------------------------+ //|Constructor | //+------------------------------------------------------------------+ CNews::CNews(void):DropRequest("PRAGMA foreign_keys = OFF; " "PRAGMA secure_delete = ON; " "Drop %s IF EXISTS '%s'; " "Vacuum; " "PRAGMA foreign_keys = ON;")//Sql drop statement { // ... string views[] = {"AU","NONE","UK","US"}; //-- Sql statement for creating the table views for each DST schedule string view_sql = "CREATE VIEW IF NOT EXISTS Calendar_%s " "AS " "SELECT C.Eventid as 'ID',C.Eventname as 'Name',C.Country as 'Country', " "(CASE WHEN Date(REPLACE(T.DST_%s,'.','-'))<R.Date THEN CONCAT(T.DST_%s,' | Yesterday') " "WHEN Date(REPLACE(T.DST_%s,'.','-'))=R.Date THEN CONCAT(T.DST_%s,' | Today') " "WHEN Date(REPLACE(T.DST_%s,'.','-'))>R.Date THEN CONCAT(T.DST_%s,' | Tomorrow') END) as " "'Date',C.EventCurrency as 'Currency',Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','')" " as 'Importance' from MQL5Calendar C,Record R Inner join TimeSchedule T on C.ID=T.ID Where" " DATE(REPLACE(T.DST_%s,'.','-'))>=DATE(R.Date,'-1 day') AND DATE(REPLACE(T.DST_%s,'.','-'))" "<=DATE(R.Date,'+1 day') Order by T.DST_%s Asc;"; // ... //--- initializing properties for the EventInfo view CalendarContents[5].Content = EventInfo_View; CalendarContents[5].name = "Event Info"; CalendarContents[5].sql = "CREATE VIEW IF NOT EXISTS 'Event Info' " "AS SELECT DISTINCT MC.EVENTID as 'ID',MC.COUNTRY as 'Country',MC.EVENTNAME as 'Name'," "REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type',REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector'," "REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance',MC.EVENTCURRENCY as 'Currency'," "REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency',MC.EVENTCODE as 'Code' " "FROM MQL5Calendar MC ORDER BY \"Country\" Asc," "CASE \"Importance\" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,\"Sector\" Desc;"; CalendarContents[5].tbl_name = "Event Info"; CalendarContents[5].type = "view"; // ... //--- initializing properties for the UpcomingEventInfo view CalendarContents[7].Content = UpcomingEventInfo_View; CalendarContents[7].name = "Upcoming Event Dates"; CalendarContents[7].sql = "CREATE VIEW IF NOT EXISTS 'Upcoming Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID as 'E_ID'," "M.COUNTRY as 'Country',M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M)," "INFO_DATE AS(SELECT E_ID,Country,Name,Currency,(SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M," "Record R INNER JOIN TIMESCHEDULE T ON T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-'))>R.Date AND " "E_ID=M.EVENTID ORDER BY Time ASC LIMIT 1) as 'Next Event Date' FROM UNIQUE_EVENTS) SELECT E_ID " "as 'ID',Country,Name,Currency,(CASE WHEN \"Next Event Date\" IS NULL THEN 'Unknown' ELSE " "\"Next Event Date\" END) as 'Upcoming Date',(CASE WHEN \"Next Event Date\"<>'Unknown' THEN " "(case cast (strftime('%w', DATE(REPLACE(\"Next Event Date\",'.','-'))) as integer) WHEN 0 THEN" " 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday'" " WHEN 5 THEN 'Friday' ELSE 'Saturday' END) ELSE 'Unknown' END) as 'Day' FROM INFO_DATE Order BY " "\"Upcoming Date\" ASC;"; CalendarContents[7].tbl_name = "Upcoming Event Dates"; CalendarContents[7].type = "view"; //--- initializing properties for the RecentEventInfo view CalendarContents[8].Content = RecentEventInfo_View; CalendarContents[8].name = "Recent Event Dates"; CalendarContents[8].sql = "CREATE VIEW IF NOT EXISTS 'Recent Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID" " as 'E_ID',M.COUNTRY as 'Country',M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency'" "FROM 'MQL5Calendar' M),INFO_DATE AS(SELECT E_ID,Country,Name,Currency," "(SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M,Record R INNER JOIN TIMESCHEDULE T ON" " T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-'))<=R.Date AND E_ID=M.EVENTID ORDER BY Time DESC" " LIMIT 1) as 'Last Event Date' FROM UNIQUE_EVENTS) SELECT E_ID as 'ID',Country,Name,Currency" ",\"Last Event Date\" as 'Recent Date',(case cast (strftime('%w', DATE(REPLACE(\"Last Event Date\"" ",'.','-'))) as integer) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN" " 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) as 'Day' FROM INFO_DATE" " Order BY \"Recent Date\" DESC;"; CalendarContents[8].tbl_name = "Recent Event Dates"; CalendarContents[8].type = "view"; // ...
Запрос ниже отвечает за создание четырех похожих, но разных представлений: Calendar_AU, Calendar_NONE, Calendar_UK и Calendar_US. Каждое из этих представлений извлекает данные из трех таблиц: MQL5Calendar, Record и TimeSchedule. Эти запросы создают представления, отображающие информацию о событиях (идентификатор, название, страна, дата, валюта и важность) для разных часовых поясов (Австралия, Нет/По умолчанию, Великобритания и США). Дата каждого события маркируется в зависимости от того, произошло ли оно вчера, сегодня или завтра относительно текущей даты, а результаты сортируются для отображения событий, происходящих в течение одного дня от текущей даты. Представления сортируются по дате события в соответствующем часовом поясе.
Для объяснения мы используем представление Calendar_AU.
Полностью видимый запрос:
CREATE VIEW IF NOT EXISTS Calendar_AU AS SELECT C.Eventid as 'ID',C.Eventname as 'Name',C.Country as 'Country', (CASE WHEN Date(REPLACE(T.DST_AU,'.','-'))<R.Date THEN CONCAT(T.DST_AU,' | Yesterday') WHEN Date(REPLACE(T.DST_AU,'.','-'))=R.Date THEN CONCAT(T.DST_AU,' | Today') WHEN Date(REPLACE(T.DST_AU,'.','-'))>R.Date THEN CONCAT(T.DST_AU,' | Tomorrow') END) as 'Date', C.EventCurrency as 'Currency',Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','') as 'Importance' from MQL5Calendar C, Record R Inner join TimeSchedule T on C.ID=T.ID Where DATE(REPLACE(T.DST_AU,'.','-'))>=DATE(R.Date,'-1 day') AND DATE(REPLACE(T.DST_AU,'.','-'))<=DATE(R.Date,'+1 day') Order by T.DST_AU Asc;
CREATE VIEW IF NOT EXISTS Calendar_AU - создает представление с именем Calendar_AU, если его еще нет. Представление — это, по сути, виртуальная таблица, созданная на основе запроса, позволяющая извлекать данные без их повторного сохранения.
Выражение SELECT:
SELECT C.Eventid as 'ID', C.Eventname as 'Name', C.Country as 'Country', (CASE WHEN Date(REPLACE(T.DST_AU,'.','-')) < R.Date THEN CONCAT(T.DST_AU, ' | Yesterday') WHEN Date(REPLACE(T.DST_AU,'.','-')) = R.Date THEN CONCAT(T.DST_AU, ' | Today') WHEN Date(REPLACE(T.DST_AU,'.','-')) > R.Date THEN CONCAT(T.DST_AU, ' | Tomorrow') END) as 'Date', C.EventCurrency as 'Currency', Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','') as 'Importance'
Эта часть запроса выбирает определенные поля из таблиц MQL5Calendar, Record и TimeSchedule и соответствующим образом форматирует данные:
- C.Eventid - идентификатор события.
- C.Eventname - имя события.
- C.Country - страна, ассоциированная с событием.
- Оператор CASE используется для сравнения даты DST_AU (австралийский часовой пояс, хранящийся в TimeSchedule) с R.Date (текущая дата из таблицы Record), чтобы обозначить событие как Yesterday (вчера), Today (сегодня) или Tomorrow (завтра).
- C.EventCurrency - валюта, связанная с событием.
- Replace(C.EventImportance,'CALENDAR_IMPORTANCE_','') удаляет префикс CALENDAR_IMPORTANCE_ из поля EventImportance, извлекая только соответствующий уровень важности (например, HIGH - высокий или LOW - низкий).
Выражение FROM:
FROM MQL5Calendar C, Record R Inner join TimeSchedule T on C.ID=T.ID
- Запрос извлекает данные из трех таблиц: MQL5Calendar (C), Record (R) и TimeSchedule (T).
- Таблицы MQL5Calendar и Record являются частью предложения FROM, а таблица TimeSchedule объединяется с помощью INNER JOIN при условии C.ID=T.ID, что означает, что идентификатор из таблицы MQL5Calendar должен совпадать с идентификатором в таблице TimeSchedule.
Выражение WHERE:
WHERE DATE(REPLACE(T.DST_AU,'.','-')) >= DATE(R.Date,'-1 day') AND DATE(REPLACE(T.DST_AU,'.','-')) <= DATE(R.Date,'+1 day')
- Это позволяет отсортировать результаты, чтобы включить только события, для которых DST_AU (дата события в австралийском часовом поясе) находится в пределах одного дня до или после текущей даты (R.Date).
Выражение ORDER BY:
ORDER BY T.DST_AU Asc;
- Это позволяет отсортировать события в порядке возрастания на основе DST_AU - даты по австралийскому времени.
Ключевые концепции:
- Форматирование даты и времени: Функция REPLACE() используется для замены точек . дефисами - в столбцах DST_, которые хранятся в виде строк, представляющих даты (например, 2024.09.23 преобразуется в 2024-09-23).
- Условная логика: оператор CASE проверяет, является ли дата события (DST_AU, DST_NONE, DST_UK или DST_US) предшествующей, равной или последующей текущей дате (R.Date), и добавляет соответствующую метку (Yesterday, Today или Tomorrow).
- Сортировка: выражение WHERE гарантирует, что в представление будут включены только события, попадающие в диапазон одного дня до или после текущей даты.
- Извлечение важности: функция REPLACE() удаляет префикс из столбца EventImportance, чтобы отобразить только соответствующий уровень важности (например, HIGH - высокий, MEDIUM - средний или LOW - низкий).
Выходные данные представления Calendar_AU:
ID Name Country Date Currency Importance 392080012 Autumnal Equinox Day Japan 2024.09.22 02:00 | Yesterday JPY NONE 554010007 Exports New Zealand 2024.09.23 00:45 | Today NZD LOW 554010008 Imports New Zealand 2024.09.23 00:45 | Today NZD LOW // ... 710010010 Heritage Day South Africa 2024.09.24 02:00 | Tomorrow ZAR NONE 36030005 RBA Rate Statement Australia 2024.09.24 07:30 | Tomorrow AUD MODERATE // ...
Запрос ниже создает представление с именем Event Info (информация о событии), которое выбирает и упорядочивает информацию о событиях из таблицы MQL5Calendar. Это представление извлекает конкретную информацию о событиях из таблицы MQL5Calendar и форматирует данные для удобства чтения и анализа. Оно очищает имена полей, удаляя ненужные префиксы (CALENDAR_TYPE_, CALENDAR_SECTOR_ и т.д.).
CREATE VIEW IF NOT EXISTS 'Event Info' AS SELECT DISTINCT MC.EVENTID as 'ID',MC.COUNTRY as 'Country',MC.EVENTNAME as 'Name', REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type',REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector', REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance',MC.EVENTCURRENCY as 'Currency', REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency',MC.EVENTCODE as 'Code' FROM MQL5Calendar MC ORDER BY "Country" Asc,CASE "Importance" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END,"Sector" Desc;
CREATE VIEW IF NOT EXISTS 'Event Info'
Эта часть создает представление под названием Event Info, если его еще не существует. Представление в SQL — это виртуальная таблица, основанная на результате запроса SELECT, позволяющая инкапсулировать сложный запрос и ссылаться на него как на таблицу.
Выражение SELECT DISTINCT:
SELECT DISTINCT MC.EVENTID as 'ID', MC.COUNTRY as 'Country', MC.EVENTNAME as 'Name', REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type', REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector', REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance', MC.EVENTCURRENCY as 'Currency', REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency', MC.EVENTCODE as 'Code'
В этом разделе извлекаются отдельные строки (без дубликатов) из таблицы MQL5Calendar (MC — псевдоним этой таблицы) и выбираются определенные столбцы для формирования представления.
Выбранные поля:
- MC.EVENTID as 'ID' - извлекает уникальный идентификатор каждого события и переименовывает его в 'ID'.
- MC.COUNTRY as 'Country' - извлекает страну, связанную с событием.
- MC.EVENTNAME as 'Name' - извлекает имя события.
Преобразование данных с использованием REPLACE():
В некоторых полях используется функция REPLACE() для удаления префиксов, таких как CALENDAR_TYPE_, CALENDAR_SECTOR_, CALENDAR_IMPORTANCE_ и CALENDAR_FREQUENCY_, оставляя только значимую часть поля:
- REPLACE(MC.EVENTTYPE,'CALENDAR_TYPE_','') as 'Type' - удаляет префикс CALENDAR_TYPE_ из поля EVENTTYPE, чтобы получить более чистое значение (например, вместо CALENDAR_TYPE_CONSUMER вы получаете CONSUMER).
- REPLACE(MC.EVENTSECTOR,'CALENDAR_SECTOR_','') as 'Sector' - удаляет префикс CALENDAR_SECTOR_ из поля EVENTSECTOR для получения имени сектора.
- REPLACE(MC.EVENTIMPORTANCE,'CALENDAR_IMPORTANCE_','') as 'Importance' - удаляет префикс CALENDAR_IMPORTANCE_ из поля EVENTIMPORTANCE, чтобы отобразить уровень важности (например, HIGH, MODERATE, LOW).
- MC.EVENTCURRENCY as 'Currency' - извлекает валюту, участвующую в событии.
- REPLACE(MC.EVENTFREQUENCY,'CALENDAR_FREQUENCY_','') as 'Frequency' - удаляет префикс CALENDAR_FREQUENCY_ из поля EVENTFREQUENCY, указывая частоту события (например, MONTHLY или QUARTERLY).
- MC.EVENTCODE as 'Code' - извлекает код события без каких-либо преобразований.
Выражение FROM:
FROM MQL5Calendar MC
Запрос извлекает данные из таблицы MQL5Calendar, имеющей псевдоним MC.
Выражение ORDER BY:
ORDER BY "Country" Asc, CASE "Importance" WHEN 'HIGH' THEN 1 WHEN 'MODERATE' THEN 2 WHEN 'LOW' THEN 3 ELSE 4 END, "Sector" Desc
В этом разделе данные упорядочиваются по нескольким полям для организации вывода представления.
Сортировка по стране (по возрастанию):
- Первый критерий сортировки — Country в порядке возрастания (Asc), то есть события группируются и сортируются в алфавитном порядке по полю страны.
Сортировка по важности (пользовательский порядок):
- Оператор CASE сортирует уровни важности на основе заданного приоритета:
- Важности 'HIGH' (высокая) присваивается значение 1 (наивысший приоритет).
- Важности 'MODERATE' (средняя) присваивается значение 2.
- Важности 'LOW' (низкая) присваивается значение 3.
- Любому другому значению (например, NONE) присваивается значение 4, что является самым низким приоритетом.
Это означает, что события с важностью HIGH будут отображаться первыми, затем следуют события MODERATE, LOW и NONE.
Сортировка по сектору (по убыванию):
- Наконец, события сортируются по полю Sector в порядке убывания (Desc). Сортировка позволяет упорядочить такие сектора, как MONEY (деньги), CONSUMER (потребитель) и т. д., в обратном алфавитном порядке.
Пример выходных данных представления Event Info:
ID Country Name Type Sector Importance Currency Frequency Code 36030008 Australia RBA Interest Rate Decision INDICATOR MONEY HIGH AUD NONE AU 36030006 Australia RBA Governor Lowe Speech EVENT MONEY HIGH AUD NONE AU 36010003 Australia Employment Change INDICATOR JOBS HIGH AUD MONTH AU // ... 36010036 Australia Current Account INDICATOR TRADE MODERATE AUD QUARTER AU 36010011 Australia Trade Balance INDICATOR TRADE MODERATE AUD MONTH AU 36010029 Australia PPI q/q INDICATOR PRICES MODERATE AUD QUARTER AU // ... 36010009 Australia Exports m/m INDICATOR TRADE LOW AUD MONTH AU 36010010 Australia Imports m/m INDICATOR TRADE LOW AUD MONTH AU 36010037 Australia Net Exports Contribution INDICATOR TRADE LOW AUD QUARTER AU // ... 76020002 Brazil BCB Interest Rate Decision INDICATOR MONEY HIGH BRL NONE BR // ...
Запрос ниже создает представление Recent Event Dates (даты последних событий), которое содержит сводку последних событий из таблицы MQL5Calendar с указанием дня недели, в который произошло событие. Это представление содержит список последних событий из таблицы MQL5Calendar с указанием дня недели, в который произошло событие. Он фокусируется на самом последнем событии для каждого отдельного события в календаре.
CREATE VIEW IF NOT EXISTS 'Recent Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID as 'E_ID',M.COUNTRY as 'Country', M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency'FROM 'MQL5Calendar' M),INFO_DATE AS(SELECT E_ID,Country,Name,Currency, (SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M,Record R INNER JOIN TIMESCHEDULE T ON T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-')) <=R.Date AND E_ID=M.EVENTID ORDER BY Time DESC LIMIT 1) as 'Last Event Date' FROM UNIQUE_EVENTS) SELECT E_ID as 'ID',Country,Name, Currency,"Last Event Date" as 'Recent Date',(case cast (strftime('%w', DATE(REPLACE("Last Event Date",'.','-'))) as integer) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) as 'Day' FROM INFO_DATE Order BY "Recent Date" DESC;
CREATE VIEW IF NOT EXISTS 'Recent Event Dates'
Эта часть создает представление под названием Recent Event Dates, если его еще не существует.
Выражение WITH: Общие табличные выражения (Common Table Expressions, CTEs)
В этой части определяются два общих табличных выражения (CTE), которые упрощают логику запроса, разбивая ее на промежуточные этапы.
CTE 1: UNIQUE_EVENTS
WITH UNIQUE_EVENTS AS ( SELECT DISTINCT M.EVENTID as 'E_ID', M.COUNTRY as 'Country', M.EVENTNAME as 'Name', M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M )
- CTE (UNIQUE_EVENTS) извлекает отдельные события из таблицы MQL5Calendar.
- Он выбирает идентификатор события (EVENTID), страну, название и валюту, гарантируя, что каждое событие будет указано только один раз (DISTINCT удаляет повторяющиеся записи).
Столбцы, выбранные в UNIQUE_EVENTS:
- M.EVENTID as 'E_ID' - уникальный идентификатор события.
- M.COUNTRY as 'Country' - страна, связанная с событием.
- M.EVENTNAME as 'Name' - название события.
- M.EVENTCURRENCY as 'Currency' - валюта, связанная с событием.
CTE 2: INFO_DATE
INFO_DATE AS ( SELECT E_ID, Country, Name, Currency, ( SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M, Record R INNER JOIN TimeSchedule T ON T.ID = M.ID WHERE DATE(REPLACE(Time, '.', '-')) <= R.Date AND E_ID = M.EVENTID ORDER BY Time DESC LIMIT 1 ) as 'Last Event Date' FROM UNIQUE_EVENTS )
CTE (INFO_DATE) добавляет поле даты (Last Event Date - дата последнего события) к отдельным событиям из предыдущего CTE. Это происходит следующим образом:
- Для каждого уникального события (E_ID, Country, Name, Currency) выбирается самая последняя дата события из таблиц TimeSchedule и MQL5Calendar.
Поля в INFO_DATE:Пояснение подзапроса:
- Подзапрос извлекает поле DST_NONE из таблицы TimeSchedule (объединенной с таблицами MQL5Calendar и Record) - временная метка или время события.
- Условие DATE(REPLACE(Time, '.', '-')) <= R.Date гарантирует, что дата события (Time, после замены точек . дефисами - для формирования корректного формата даты) меньше или равна текущей дате в таблице записей (R.Date).
- События сортируются в порядке убывания времени (ORDER BY Time DESC), а LIMIT 1 гарантирует, что будет извлечено только самое последнее время события.
- E_ID - идентификатор события из UNIQUE_EVENTS CTE.
- Country - страна из UNIQUE_EVENTS CTE.
- Name - имя события из UNIQUE_EVENTS CTE.
- Currency - валюта из UNIQUE_EVENTS CTE.
- Last Event Date - самая последняя дата события для каждого события, извлеченная из таблиц MQL5Calendar и TimeSchedule.
Основной запрос: Окончательный выбор и преобразование
SELECT E_ID as 'ID', Country, Name, Currency, "Last Event Date" as 'Recent Date', ( CASE CAST (strftime('%w', DATE(REPLACE("Last Event Date", '.', '-'))) AS INTEGER) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END ) as 'Day' FROM INFO_DATE ORDER BY "Recent Date" DESC;
Основная часть запроса выбирает конечный результат из CTE INFO_DATE и включает следующие поля:
- E_ID as 'ID' - идентификатор события (ID).
- Country - страна, связанная с событием.
- Name - имя события.
- Currency - валюта, связанная с событием.
- "Last Event Date" as 'Recent Date' - дата самого последнего события, переименованная в 'Recent Date'.
Расчет дня недели:
- Запрос использует strftime('%w', DATE(REPLACE("Last Event Date", '.', '-'))), которая преобразует 'Last Event Date' (дата последнего события) в допустимый формат даты и извлекает день недели.
- %w возвращает целое число, представляющее день недели, где 0 = Sunday (воскресенье), 1 = Monday (понедельник), ..., 6 = Saturday (суббота).
- Оператор CASE сопоставляет целое число с соответствующим названием дня (например, 0 -> Sunday, 1 -> Monday и т.д.).
Сортировка результатов:
ORDER BY "Recent Date" DESC
Результаты сортируются по Recent Date в порядке убывания, то есть самые последние события отображаются в верхней части списка.
Пример выходных данных представления Recent Event Dates:
ID Country Name Currency Recent Date Day 554520001 New Zealand CFTC NZD Non-Commercial Net Positions NZD 2024.09.27 21:30 Friday 999520001 European Union CFTC EUR Non-Commercial Net Positions EUR 2024.09.27 21:30 Friday 392520001 Japan CFTC JPY Non-Commercial Net Positions JPY 2024.09.27 21:30 Friday // ...
Приведенный ниже запрос создает представление под названием Upcoming Event Dates (даты предстоящих событий) для вывода списка предстоящих событий из таблицы MQL5Calendar. Он включает в себя следующую дату мероприятия и день недели. Запрос состоит из двух ключевых частей: сначала он идентифицирует уникальные события, затем определяет следующую запланированную дату события для каждого из этих событий.
CREATE VIEW IF NOT EXISTS 'Upcoming Event Dates' AS WITH UNIQUE_EVENTS AS(SELECT DISTINCT M.EVENTID as 'E_ID',M.COUNTRY as 'Country',M.EVENTNAME as 'Name',M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M),INFO_DATE AS(SELECT E_ID,Country,Name,Currency,(SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M,Record R INNER JOIN TIMESCHEDULE T ON T.ID=M.ID WHERE DATE(REPLACE(Time,'.','-'))>R.Date AND E_ID=M.EVENTID ORDER BY Time ASC LIMIT 1) as 'Next Event Date' FROM UNIQUE_EVENTS) SELECT E_ID as 'ID',Country,Name,Currency,(CASE WHEN "Next Event Date" IS NULL THEN 'Unknown' ELSE "Next Event Date" END) as 'Upcoming Date',(CASE WHEN "Next Event Date"<>'Unknown' THEN (case cast (strftime('%w', DATE(REPLACE("Next Event Date",'.','-'))) as integer) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) ELSE 'Unknown' END) as 'Day' FROM INFO_DATE Order BY "Upcoming Date" ASC;
CREATE VIEW IF NOT EXISTS 'Upcoming Event Dates'
Это создает представление с названием Upcoming Event Dates (даты предстоящих событий), но только если оно еще не существует.
Выражение WITH: Общие табличные выражения (Common Table Expressions, CTEs)
Запрос использует общие табличные выражения (CTE), которые помогают разбить сложные запросы на более простые части, пригодные для повторного использования. Здесь есть два CTE: UNIQUE_EVENTS и INFO_DATE.
CTE 1: UNIQUE_EVENTS
WITH UNIQUE_EVENTS AS ( SELECT DISTINCT M.EVENTID as 'E_ID', M.COUNTRY as 'Country', M.EVENTNAME as 'Name', M.EVENTCURRENCY as 'Currency' FROM 'MQL5Calendar' M )
- Эта часть выбирает уникальные события из таблицы MQL5Calendar.
- Она извлекает идентификатор события (EVENTID), страну, название и валюту, гарантируя, что каждое событие будет указано только один раз с помощью DISTINCT.
Результатом этого CTE является набор отдельных событий с соответствующими им подробностями.
Столбцы в UNIQUE_EVENTS:
- E_ID - уникальный идентификатор события.
- Country - страна, в которой произошло событие.
- Name - имя события.
- Currency - валюта, связанная с событием.
CTE 2: INFO_DATE
INFO_DATE AS ( SELECT E_ID, Country, Name, Currency, ( SELECT T.DST_NONE as 'Time' FROM MQL5Calendar M, Record R INNER JOIN TimeSchedule T ON T.ID=M.ID WHERE DATE(REPLACE(Time, '.', '-')) > R.Date AND E_ID = M.EVENTID ORDER BY Time ASC LIMIT 1 ) as 'Next Event Date' FROM UNIQUE_EVENTS )
CTE (INFO_DATE) извлекает дату следующего события для каждого уникального события.
- Для каждого события (E_ID, Country, Name, Currency) ищется дата предстоящего события в таблице TimeSchedule table (поле DST_NONE).
Пояснение подзапроса:
- Подзапрос извлекает поле DST_NONE из таблицы TimeSchedule - время или дата события.
- Условие DATE(REPLACE(Time, '.', '-')) > R.Date гарантирует, что будут выбраны только будущие события (даты больше текущей, R.Date).
- События сортируются в порядке возрастания по времени (ORDER BY Time ASC), поэтому выбирается самая ранняя дата предстоящего события (LIMIT 1 гарантирует, что будет возвращена только одна дата).
Столбцы в INFO_DATE:
- E_ID - идентификатор события из UNIQUE_EVENTS.
- Country - страна, связанная с событием.
- Name - имя события.
- Currency - валюта события.
- Next Event Date - дата следующего предстоящего события, определяемая подзапросом.
Основной запрос: Окончательный выбор и преобразование
SELECT E_ID as 'ID', Country, Name, Currency, (CASE WHEN "Next Event Date" IS NULL THEN 'Unknown' ELSE "Next Event Date" END) as 'Upcoming Date', (CASE WHEN "Next Event Date" <> 'Unknown' THEN (CASE CAST (strftime('%w', DATE(REPLACE("Next Event Date", '.', '-'))) AS INTEGER) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END) ELSE 'Unknown' END) as 'Day' FROM INFO_DATE ORDER BY "Upcoming Date" ASC;
Основной запрос извлекает окончательный вывод из CTE INFO_DATE, преобразуя результаты и добавляя дополнительную логику для обработки случаев, когда могут отсутствовать даты событий (значения NULL).
Выбранные столбцы:
- E_ID as 'ID' - идентификатор события (ID).
- Country - страна, связанная с событием.
- Name - имя события.
- Currency - валюта, связанная с событием.
- Upcoming Date - предстоящая дата на основе Next Event Date (дата следующего события). Если Next Event Date равна NULL, выводится 'Unknown'; в противном случае будет показана дата.
CASE WHEN "Next Event Date" IS NULL THEN 'Unknown' ELSE "Next Event Date" END
Оператор CASE проверяет, существует ли допустимая дата предстоящего события. Если дата равна NULL, выводится 'Unknown'; в противном случае отображается Next Event Date (дата следующего события).
Расчет дня недели:
- Если Next Event Date не 'Unknown', запрос преобразует предстоящую дату в день недели с помощью следующего оператора CASE:
CASE CAST (strftime('%w', DATE(REPLACE("Next Event Date", '.', '-'))) AS INTEGER) WHEN 0 THEN 'Sunday' WHEN 1 THEN 'Monday' WHEN 2 THEN 'Tuesday' WHEN 3 THEN 'Wednesday' WHEN 4 THEN 'Thursday' WHEN 5 THEN 'Friday' ELSE 'Saturday' END
Функция strftime('%w', ...) извлекает день недели из Next Event Date:
- %w возвращает целое число, представляющее день недели (0 = Sunday, 1 = Monday, etc.).
- Оператор CASE сопоставляет это целое число с названием дня (например, 0 -> Sunday).
- Если Next Event Date равен 'Unknown', в столбце Day также отобразиться 'Unknown'.
Упорядочивание результатов:
ORDER BY "Upcoming Date" ASC;
Результаты сортируются по Upcoming Date (предстоящая дата) в порядке возрастания, то есть первыми отображаются самые ранние предстоящие события.
Пример выходных данных представления Upcoming Date:
ID Country Name Currency Upcoming Date Day 410020004 South Korea Industrial Production y/y KRW 2024.09.30 01:00 Monday 410020005 South Korea Retail Sales m/m KRW 2024.09.30 01:00 Monday 410020006 South Korea Index of Services m/m KRW 2024.09.30 01:00 Monday // ... 36500001 Australia S&P Global Manufacturing PMI AUD 2024.10.01 01:00 Tuesday 392030007 Japan Unemployment Rate JPY 2024.10.01 01:30 Tuesday 392050002 Japan Jobs to Applicants Ratio JPY 2024.10.01 01:30 Tuesday // ...
Код советника
Это основной файл программы, в котором мы реализуем стратегию торговли на новостях. Приведенный ниже код позволяет вводить необходимые параметры торговли.
input Choice iCustom_Event_1=No;//USE EVENT IDs BELOW? input string iCustom_Event_1_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_2=No;//USE EVENT IDs BELOW? input string iCustom_Event_2_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_3=No;//USE EVENT IDs BELOW? input string iCustom_Event_3_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_4=No;//USE EVENT IDs BELOW? input string iCustom_Event_4_IDs="";//EVENT IDs[Separate with a comma][MAX 14] input Choice iCustom_Event_5=No;//USE EVENT IDs BELOW? input string iCustom_Event_5_IDs="";//EVENT IDs[Separate with a comma][MAX 14]
Объяснение каждого параметра:
Choice iCustom_Event_1=No;
- Тип: Choice
- Имя переменной: iCustom_Event_1
- Значение по умолчанию: No
- Описание: Этот входной параметр позволяет пользователю включить или отключить использование пользовательских идентификаторов событий для события 1. Тип Choice здесь — тип перечисления с двумя значениями: Yes или No. При Yes программа будет использовать идентификаторы событий, указанные в соответствующем строковом вводе (iCustom_Event_1_IDs).
string iCustom_Event_1_IDs="";
- Тип: string
- Имя переменной: iCustom_Event_1_IDs
- Значение по умолчанию: пустая строка ""
- Описание: Входной параметр позволяет пользователю ввести список идентификаторов событий для пользовательского события 1. Ожидается, что эти идентификаторы будут разделены запятыми (например, "36010006,840030005,840030016") и будет указано максимум 14 идентификаторов.
Инициализация пользовательских новостных событий
CEvent1.useEvents = Answer(iCustom_Event_1); StringSplit(iCustom_Event_1_IDs, ',', CEvent1.EventIds); CEvent2.useEvents = Answer(iCustom_Event_2); StringSplit(iCustom_Event_2_IDs, ',', CEvent2.EventIds); CEvent3.useEvents = Answer(iCustom_Event_3); StringSplit(iCustom_Event_3_IDs, ',', CEvent3.EventIds); CEvent4.useEvents = Answer(iCustom_Event_4); StringSplit(iCustom_Event_4_IDs, ',', CEvent4.EventIds); CEvent5.useEvents = Answer(iCustom_Event_5); StringSplit(iCustom_Event_5_IDs, ',', CEvent5.EventIds);
Блок кода инициализирует несколько пользовательских объектов новостных событий (CEvent1, CEvent2 и т.д.) и обрабатывает их связанные идентификаторы.
- CEvent1.useEvents = Answer(iCustom_Event_1);:
- CEvent1 — это структура, которая содержит информацию о конкретном наборе пользовательских новостных событий. Флаг useEvents устанавливается с помощью функции Answer(), которая возвращает логическое значение (true или false) на основе входной переменной iCustom_Event_1.
- Если iCustom_Event_1 равен true, советник будет использовать это пользовательское событие для торговой логики, в противном случае он будет игнорировать его.
- StringSplit(iCustom_Event_1_IDs, ',', CEvent1.EventIds);:
- StringSplit() — это функция, которая разбивает строку идентификаторов событий (iCustom_Event_1_IDs) запятой (,), а полученный список идентификаторов событий сохраняется в CEvent1.EventIds.
- iCustom_Event_1_IDs — это строка, содержащая один или несколько идентификаторов событий, а функция split преобразует эту строку в массив идентификаторов событий для дальнейшего использования в торговой логике.
Аналогичный код для CEvent2 – CEvent5.
Функция OnInit()
Это функция инициализации, которая вызывается при запуске советника или добавлении его на график.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Assign if in LightMode or not isLightMode=(iDisplayMode==Display_LightMode)?true:false; //--- call function for common initialization procedure InitCommon(); //--- store Init result int InitResult; if(!MQLInfoInteger(MQL_TESTER))//Checks whether the program is in the strategy tester { //--- initialization procedure outside strategy tester InitResult=InitNonTester(); } else { //--- initialization procedure inside strategy tester InitResult=InitTester(); } //--- Create DB in memory NewsObject.CreateEconomicDatabaseMemory(); //--- Initialize Candle properties pointer object CP = new CCandleProperties(); //--- Retrieve news events for the current Daily period into array CalendarArray NewsObject.EconomicDetailsMemory(CalendarArray,CTM.Time(TimeTradeServer(),0,0,0), (iOrderType!=StopOrdersType)?true:false); //--- Initialize Common graphics class pointer object CGraphics = new CCommonGraphics(Answer(iDisplay_Date),Answer(iDisplay_Spread), Answer(iDisplay_NewsInfo),Answer(iDisplay_EventObj)); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create chart objects //--- Set Time CDay.SetmyTime(CalendarArray); /* create timer, if in the strategy tester set the timer to 30s else 100ms */ EventSetMillisecondTimer((!MQLInfoInteger(MQL_TESTER))?100:30000); //-- Initialize Trade Management class pointer object Trade = new CTradeManagement(iDeviation); //--- return Init result return InitResult; }
Ключевые шаги:
Режим отображения:
- Советник проверяет, работает ли он в светлом или темном режиме (isLightMode).
Общая инициализация:
- Вызывает функцию InitCommon() для выполнения общих задач инициализации.
Проверка тестера стратегий:
- Проверяет, работает ли советник в режиме тестера стратегий, используя MQLInfoInteger(MQL_TESTER), а затем вызывает либо InitNonTester(), либо InitTester() в зависимости от результата.
База данных новостных событий:
- Функция NewsObject.CreateEconomicDatabaseMemory() инициализирует базу данных экономических событий в памяти. Именно здесь советник хранит данные, связанные с новостями.
Инициализация свойств свечей:
- Создается указатель класса CP для CCandleProperties. Класс отвечает за управление свойствами свечей, такими как открытие, закрытие, максимум, минимум и т. д.
Получение новостных событий:
- Функция NewsObject.EconomicDetailsMemory() извлекает соответствующие новостные события на основе выбранных критериев. Функция сортирует новостные события за текущий торговый день.
Инициализация графических объектов:
- Инициализируется класс CGraphics, который отвечает за создание графических элементов на графике, например, визуализацию данных новостных событий. Метод GraphicsRefresh() обеспечивает обновление графических объектов в соответствии с настроенным временем (iSecondsPreEvent).
Установка времени и таймера:
- Метод CDay.SetmyTime() обрабатывает массив новостных событий и осуществляет управление временем для торговли.
- Таймер устанавливается с разными интервалами в зависимости от того, запущен ли советник в тестере или в реальном времени (EventSetMillisecondTimer()), это необходимо для обеспечения производительности при тестировании советника в тестере стратегий.
Инициализация управления торговлей:
- Указатель класса Trade инициализируется с помощью iDeviation для открытия сделок со стоп-ордерами при необходимости.
Возврат результата инициализации:
- Функция возвращает результат инициализации.
Функция OnTimer
Функция OnTimer() активируется периодически, выполняясь каждый раз при вызове события таймера.
//+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { if(((!MQLInfoInteger(MQL_TESTER))?int(TimeTradeServer())%30==0:true)) { //--- Store start-up time. static datetime Startup_date = TimeTradeServer(); if(CTM.DateisToday(Startup_date)&&CP.NewCandle(0,PERIOD_D1) &&MQLInfoInteger(MQL_TESTER)) { //--- Retrieve news events for the current Daily period into array CalendarArray NewsObject.EconomicDetailsMemory(CalendarArray,CTM.Time(TimeTradeServer(),0,0,0), (iOrderType!=StopOrdersType)?true:false); //--- Initialize Common graphics class pointer object CGraphics = new CCommonGraphics(Answer(iDisplay_Date),Answer(iDisplay_Spread), Answer(iDisplay_NewsInfo),Answer(iDisplay_EventObj)); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create chart objects //--- Set Time CDay.SetmyTime(CalendarArray); } //--- Run procedures ExecutionOnTimer(Startup_date); if(CTS.isSessionStart()&&!CTS.isSessionEnd()) { //--- function to open trades TradeTime(); } //--- close trades within 45 min before end of session if(CTS.isSessionStart()&&CTS.isSessionEnd()&&!CTS.isSessionEnd(0,0)) { Trade.CloseTrades("NewsTrading"); } } }
Основные особенности:
- Условия, основанные на времени: оператор if проверяет, выполняется ли код в тестере стратегий или в режиме реального времени. При работе в реальном времени (вне тестера стратегий) проверяется, делится ли время сервера на 30 (для 30-секундного интервала).
- Обновление новостей и свечей:
- Если сегодняшняя дата совпадает с Startup_date. Если сегодня - новый день и сформировалась новая дневная свеча (CP.NewCandle(0, PERIOD_D1)), советник извлекает экономические новости за текущий день, используя NewsObject.EconomicDetailsMemory.
- График также обновляется.
- Управление сессией: если торговая сессия началась (CTS.isSessionStart()), то сделки будут разрешены во время сессии. Советник также закроет сделки, если сессия подходит к концу.
Функция ExecutionOnTimer
//+------------------------------------------------------------------+ //|Execute program procedures in time intervals | //+------------------------------------------------------------------+ void ExecutionOnTimer(datetime Startup_date) { //--- Check if not start-up date if(!CTM.DateisToday(Startup_date)) { //--- Run every New Daily Candle if(CP.NewCandle(1,PERIOD_D1)) { //--- Check if not in strategy tester if(!MQLInfoInteger(MQL_TESTER)) { //--- Update/Create DB in Memory NewsObject.CreateEconomicDatabaseMemory(); } //--- retrieve news events for the current day NewsObject.EconomicDetailsMemory(CalendarArray,CTM.Time(TimeTradeServer(),0,0,0), (iOrderType!=StopOrdersType)?true:false); //--- Set time from news events CDay.SetmyTime(CalendarArray); CGraphics.GraphicsRefresh(iSecondsPreEvent);//-- Create/Re-create chart objects } //--- Check if not in strategy tester if(!MQLInfoInteger(MQL_TESTER)) { //--- Run every New Hourly Candle if(CP.NewCandle(2,PERIOD_H1)) { //--- Check if DB in Storage needs an update if(NewsObject.UpdateRecords()) { //--- initialization procedure outside strategy tester InitNonTester(); } } } } else { //--- Run every New Daily Candle if(CP.NewCandle(3,PERIOD_D1)) { //--- Update Event objects on chart CGraphics.NewsEvent(); } } //--- Update realtime Graphic every 1 min if(CP.NewCandle(4,PERIOD_M1)) { //--- get the news events for the next min ahead of time. datetime Time_ahead = TimeTradeServer()+CTM.MinutesS(); CDay.GetmyTime(CTV.Hourly(CTM.ReturnHour(Time_ahead)), CTV.Minutely(CTV.Minutely(CTM.ReturnMinute(Time_ahead))), myTimeData,myEvents); CGraphics.Block_2_Realtime(iSecondsPreEvent); } }
Основные особенности:
- Ежедневное обновление свечей: проверяет наличие новых дневных свечей. Если сформировалась новая дневная свеча, обновляется база данных новостей (NewsObject.CreateEconomicDatabaseMemory()) и извлекаются новостные события за день.
- Обновление часовой свечи: если формируется новая часовая свеча, советник проверяет, требует ли база данных экономических новостей обновления, и если да, то повторно инициализирует советник вне режима тестера стратегий (InitNonTester()).
- Обновления в реальном времени: Каждую минуту советник обновляет графическую информацию в реальном времени и извлекает новостные события на следующую минуту, готовясь к торговле.
Функция TradeTime
Функция управляет исполнением сделок во время новостных событий.
//+------------------------------------------------------------------+ //|function to check trading time | //+------------------------------------------------------------------+ void TradeTime() { //--- Iterate through the event times for(uint i=0;i<myTimeData.Size();i++) { //--- Check if it is time to trade each news event if(CTM.TimePreEvent(CTM.TimeMinusOffset(datetime(myEvents[i].EventDate),iSecondsPreEvent) ,datetime(myEvents[i].EventDate)) &&(CTM.isDayOfTheWeek(TradingDay)||iNewSelection==News_Select_Custom_Events)) { //--- switch for order type selection switch(iOrderType) { case StopOrdersType:// triggers for STOP ORDERS StopOrders(myEvents[i]); break; default:// triggers for both MARKET POSITION & SINGLE STOP ORDER SingleOrder(myEvents[i]); break; } } } }
Основные особенности:
- Проверка времени события: для каждого события в массиве myTimeData советник проверяет, находится ли текущее время в пределах предопределенного временного окна "предсобытия" (iSecondsPreEvent) до фактического события.
- Сортировка по дню недели: сделки открываются только в том случае, если текущий день совпадает с настроенным торговым днем (TradingDay) или если выбраны пользовательские события (iNewSelection == News_Select_Custom_Events).
- Выбор типа ордера: на основе входных данных iOrderType (рыночная позиция или стоп-ордер(ы)) функция открывает либо рыночные ордера, либо стоп-ордера в период новостного события.
Функция SingleOrder
Функция открывает единые рыночные или стоп-ордера в зависимости от влияния новостного события.
//+------------------------------------------------------------------+ //|function to open single order types | //+------------------------------------------------------------------+ void SingleOrder(Calendar &NewsEvent) { //--- Check each Impact value type switch(NewsObject.IMPACT(NewsEvent.EventImpact)) { //--- When Impact news is negative case CALENDAR_IMPACT_NEGATIVE: //--- Check if profit currency is news event currency if(NewsEvent.EventCurrency==CSymbol.CurrencyProfit()) { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open buy trade with Event id as Magic number Trade.Buy(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open buy-stop with Event id as Magic number Trade.OpenBuyStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } else { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open sell trade with Event id as Magic number Trade.Sell(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open buy-stop with Event id as Magic number Trade.OpenSellStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } break; //--- When Impact news is positive case CALENDAR_IMPACT_POSITIVE: //--- Check if profit currency is news event currency if(NewsEvent.EventCurrency==CSymbol.CurrencyProfit()) { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open sell trade with Event id as Magic number Trade.Sell(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open sell-stop with Event id as Magic number Trade.OpenSellStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } else { switch(iOrderType) { case MarketPositionType:// triggers for MARKET POSITION //--- Open buy trade with Event id as Magic number Trade.Buy(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-"+NewsEvent.EventCode); break; case StopOrderType:// triggers for SINGLE STOP ORDER //--- Open sell-stop with Event id as Magic number Trade.OpenBuyStop(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-SStop-"+NewsEvent.EventCode); break; default: break; } } break; //--- Unknown default: break; } }
Основные особенности:
- Оценка воздействия: советник оценивает воздействие новостного события (NewsObject.IMPACT(NewsEvent.EventImpact)), которое может быть как отрицательным, так и положительным.
- Отрицательное влияние: если влияние новостей отрицательное и валюта события совпадает с валютой прибыли счета, открывается сделка на покупку, если iOrderType — рыночная позиция или стоп-ордер на покупку для стоп-ордеров. Если валюта события не совпадает с валютой прибыли, открывается сделка на продажу или стоп-ордер на продажу.
- Положительное влияние: если влияние новостей положительное и валюта события совпадает с валютой прибыли, открывается сделка на продажу или стоп-ордер на продажу. Если валюта события не совпадает, открывается сделка на покупку или стоп-ордер на покупку.
Функция StopOrders
Функция управляет открытием стоп-ордеров на покупку и продажу одновременно в период новостного события. Независимо от последствий события оба типа стоп-ордеров размещаются с целью уловить движение цен в любом направлении.
//+------------------------------------------------------------------+ //|function to open orders | //+------------------------------------------------------------------+ void StopOrders(Calendar &NewsEvent) { //--- Opens both buy-stop & sell-stop regardless of event impact Trade.OpenStops(iStoploss,iTakeprofit,ulong(NewsEvent.EventId), "NewsTrading-Stops-"+NewsEvent.EventCode); }
Основные особенности:
- Стоп-ордера на покупку и продажу: открывает оба типа стоп-ордеров, каждый из которых связан с определенным событием, при этом EventId события используется в качестве магического числа сделки.
Функция OnTrade
Функция срабатывает всякий раз, когда происходит новое торговое событие. Она будет использоваться для управления сделками.
void OnTrade() { //--- Check if time is within the trading session if(CTS.isSessionStart() && !CTS.isSessionEnd(0,0)) { //--- Run procedures ExecutionOnTrade(); } }
- CTS.isSessionStart() and !CTS.isSessionEnd(0,0):
- CTS (объект класса сессий) используется для проверки того, попадает ли текущее время в активную торговую сессию.
- isSessionStart() проверяет, началась ли сессия.
- !CTS.isSessionEnd(0,0) проверяет, не завершилась ли сессия. Аргументы 0,0 представляют смещение или период буфера до завершения сессии.
- Это гарантирует, что сделки корректируются только в том случае, если текущее время находится в пределах активной торговой сессии.
- ExecutionOnTrade();:
- Если сессия активна, вызывается функция ExecutionOnTrade() для управления необходимыми процедурами, связанными с новой сделкой.
Функция ExecutionOnTrade
Функция содержит логику, которая запускается каждый раз при совершении новой сделки.
void ExecutionOnTrade() { //--- if stop orders, enable fundamental mode if(iOrderType == StopOrdersType) { Trade.FundamentalMode("NewsTrading"); } //--- when stop order(s), enable slippage reduction if(iOrderType != MarketPositionType) { Trade.SlippageReduction(iStoploss, iTakeprofit, "NewsTrading"); } }
- if (iOrderType == StopOrdersType):
- Функция проверяет, является ли текущая сделка стоп-ордером (StopOrdersType). Если условие истинно, код вызывает функцию Trade.FundamentalMode().
- Trade.FundamentalMode("NewsTrading");:
- Эта функция включает фундаментальный режим (Fundamental Mode) и отвечает за удаление отложенных ордеров в противоположном направлении.
- "NewsTrading" - торговая метка/комментарий для советника.
- if(iOrderType != MarketPositionType):
- Это условие проверяет, не является ли входная переменная перечислением MarketPositionType. При true код позволяет уменьшить проскальзывание.
- Trade.SlippageReduction(iStoploss, iTakeprofit, "NewsTrading"):
- Функция уменьшает проскальзывание для нерыночных ордеров.
- Проскальзывание происходит, когда цена исполнения отличается от ожидаемой цены, особенно во время волатильных рынков или новостных событий.
- Вызывая SlippageReduction(), советник пытается минимизировать это проскальзывание.
- Параметры:
- iStoploss - значение стоп-лосса, представляющее собой цену, при которой сделка должна автоматически закрыться, чтобы ограничить потенциальные убытки.
- iTakeprofit - значение тейк-профита, представляющее собой цену, при которой сделка должна автоматически закрыться для получения прибыли.
- "NewsTrading" - торговая метка/комментарий для советника.
Заключение
В этой статье советник позволяет пользователям определять пользовательские новостные события, на которых советник будет торговать. Эти события инициализируются с помощью вводимых пользователем данных, которые затем анализируются и обрабатываются соответствующим образом советником. База данных календаря в хранилище была улучшена для отображения дополнительной информации о предстоящих и недавних событиях в виде представлений, каждый запрос представления был подробно объяснен. Для управления выполнением кода на основе определенных временных условий были добавлены функции OnTimer и OnTrade. Спасибо за внимание! До встречи в новой статье!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16170
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.






- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования