
Если ваша торговая система не базируется на новостных скачках, то бывает очень полезно для финансового и ментального здоровья останавливать работу советника перед выходом важных новостей. А после устаканивания волатильности, через нескольких часов, заново его включать. Хорошо, если у вас под рукой доступ к рабочему терминалу. А если нет? Вот тут и пригодится автоматизация такой деятельности.
Одним из вариантов решения нашей задачи является составление файла с расписанием важных событий. Чтение этого файла нашим экспертом, и определение когда можно работать, а когда нет. В учебнике MQL4 приводится пример работы с текстовым файлом csv, его то мы и возьмем за основу, попутно избавившись от пары ошибок, которые авторы допустили непреднамеренно или с целью проверки нашей внимательности (о чем они сами предупреждают — не следует слепо верить никому, ошибки бывают всегда).
Итак, составим расписание важных событий на следующей неделе в следующем формате:
Дата и время события;инструмент;описание события
2016.04.01 14:00;USD;Индекс деловой активности ISM в производственном секторе 2016.04.01 14:00;USD;Индекс постепенного разгона инфляции от ISM 2016.04.04 09:00;EUR;Индекс цен производителей (м/м) 2016.04.04 09:00;EUR;Индекс цен производителей (г/г) 2016.04.05 04:30;AUD;Решение Резервного Банка Австралии по процентной ставке 2016.04.05 04:30;AUD;Сопроводительное заявление Резервного Банка Австралии 2016.04.06 07:00;EUR;Заседание ЕЦБ 2016.04.06 18:00;USD;Протокол заседания Комитета по открытым рынкам ФРС США 2016.04.07 11:30;EUR;Сведения о заседании ЕЦБ по монетарной политике
Запишем его в файл ht-news.csv и разместим в нужной директории \MQL4\Files\ht-news.csv, откуда наш советник сможет его прочитать.
Вначале зададим внешние переменные для настройки, комментарии объясняют их назначение:
extern string startNewsEvent="Ограничение работы по новостям"; input bool StopOnNewsEvent=true; //Включить ограничение input string NewsEventFileName="ht-news.csv"; //Файл с расписанием input uint LoadNewsEventTimerInDay=7;//Как часто в днях перезагружать файл input uint StopPriorEventHours=2; //Остановить за Х часов до события input uint StartAfterEventHours=3; //Запустить через Х часов после события extern string stopNewsEvent="----------------";
Следующая структура поможет нам получить доступ к описанию обрабатываемого события:
struct NEWS_EVENT { datetime EventTime; //время наступления события string Instrument; //инструмент события string EventComment; //комментарий события };
Пользовательская функция чтения информации из файла
bool LoadNews() { int handle; // Файловый описатель string instrument, // Название валюты события firstInstrument,secondInstrument, // 1я и 2я часть названия текущей пары eventComment, // Текст описания события stringDateTime; // Строковое выражение даты и времени события datetime eventDateTime; // Дата и время события в формате datetime handle=FileOpen(NewsEventFileName,FILE_CSV|FILE_READ,";");// Открытие файла if(handle<0) // Неудача при открытии файла { int lastError=GetLastError(); if(lastError==4103) // Если файла не существует, сообщим Alert("Нет файла с именем ",NewsEventFileName); else // При любой другой ошибке Alert("Ошибка при открытии файла ",NewsEventFileName," :",lastError); PlaySound("Bzrrr.wav"); // Громко ругнемся и вернем фалсе return false; } int cnt=0; while(FileIsEnding(handle)==false) { stringDateTime =FileReadString(handle);// Дата и время события instrument=FileReadString(handle); //Инструмент eventComment =FileReadString(handle);// Текст описания события, может не быть eventDateTime =StrToTime(stringDateTime); // Преобразование типа данных firstInstrument=StringSubstr(Symbol(),0,3);// Извлекаем первые 3 символа secondInstrument=StringSubstr(Symbol(),3,3);// Извлекаем вторые 3 символа if(StringCompare(instrument,firstInstrument,false)!=0 && StringCompare(instrument,secondInstrument,false)!=0) { continue; //не наши инструменты } cnt++; ArrayResize(NewsEvent,cnt,1000); //изменяем размер массива с запасом для ускорения ArrayResize(NewsEventTime,cnt,1000); NewsEvent[cnt-1].EventTime= eventDateTime; NewsEvent[cnt-1].Instrument=instrument; NewsEvent[cnt-1].EventComment=eventComment; NewsEventTime[cnt-1]=eventDateTime; //удобнее искать в одномерном массиве } FileClose( handle ); // Закрываем файл ArraySort(NewsEventTime,WHOLE_ARRAY,0,MODE_ASCEND); //сортируем массив для последующего поиска return true; }
Поиск нужного времени осуществляем в массиве NewsEventTime, и если время найдено, то получим его описание их массива структуры NewsEvent
string GetEventInfo(datetime date) { for(int i=0;i<ArraySize(NewsEvent);i++) { if (NewsEvent[i].EventTime==date) { return (NewsEvent[i].Instrument+" "+NewsEvent[i].EventComment); } } return ""; }
Вспомогательная функция для определения, попадает ли текущее время в промежуток простоя
//проверим, не нужно ли отключить советник по новости True -работаем False - отдыхаем bool CheckEnableNewsWork() { if (ArraySize(NewsEventTime)==0) return true; //по какой-то причине массив с расписанием оказался пустой, значит работаем //теперь нужно найти ближайшую дату в NewsEventTime перед которой нужно остановить работу за StopPriorEventHours часов datetime timeCurrent =TimeCurrent(); datetime timeStopWork=timeCurrent+StopPriorEventHours*3600; //Если в NewsEventTime есть время, меньшее timeStopWork, то прекращаем работать int dateindex=ArrayBsearch(NewsEventTime,timeStopWork,WHOLE_ARRAY,0,MODE_ASCEND); datetime foundEventTime=NewsEventTime[dateindex]; timeStopWork=foundEventTime-StopPriorEventHours*3600; //посчитаем время остановки от найденного времени datetime timeStartWork=foundEventTime+StartAfterEventHours*3600; //это время окончания отдыха //теперь проверим, находимся ли мы сейчас timeCurrent между timeStopWork и timeStartWork. Если да, то не работаем if (timeCurrent>=timeStopWork && timeCurrent<=timeStartWork) { DrawLabel("EnableNewsWork","Не работаем, новость: "+GetEventInfo(foundEventTime)+"",5,80,Red); return false; } else { DrawLabel("EnableNewsWork","Нормальная работа, новостей нет",5,80,Green); return true; } }
Для периодического чтения из файла с расписанием нам нужен таймер, запускаемый к примеру раз в неделю, как настроено
//Проверяем наличие файла с новостями по таймеру void OnTimer() { if (StopOnNewsEvent) { LoadNews(); //читаем файл } }
При инициализации советника нужно запустить таймер и первый раз прочитать новости
OnInit() .... if (StopOnNewsEvent) //Если задано, загрузим файл с новостями { bool tm=EventSetTimer(LoadNewsEventTimerInDay*86400); //запустим таймер для загрузки файла с расписанием if (!tm) Alert("EventSetTimer error:",GetLastError()); OnTimer();//Первый запуск сразу }
В каждом тике проверяем, можем ли мы открывать позиции. На трейлинг кстати это влиять не должно.
OnTick() .... bool enableNewsWork=true; //можно работать по новостным событиям if (StopOnNewsEvent) { enableNewsWork=CheckEnableNewsWork(); //Если ограничение по новостям, проверим время } if (HaveMoney && enableNewsWork) Traiding(Pairs[0]); //проверка условий и открытие торговли по паре
Вот и все. К сожалению, на реальном счете я этот код пока не тестировал, так как писал в выходные, тики эмулировал таймером и текущее время, которое останавливается в выходные, менял вручную. Но на следующей неделе посмотрю уже в реале, в новой версии «Forex Grail 1.11» , куда я и встроил вышеприведенный код.
В дальнейшем, возможно, нужно сделать удобную утилиту для формирования файла ht-news.csv, например в той же программе Forex-Helper.
Предыдущая часть 8: Пишем Демо Версию.