
Создание новостного торгового советника
Введение
Как указано в Инвестопедии, трейдер, торгующий на новостях, – это "трейдер или инвестор, принимающий торговые или инвестиционные решения на основе сводок новостей". Действительно, выход экономических отчетов, включая ВВП стран, индексы потребительского доверия, данные по безработице в том или ином государстве и многие другие, часто приводит к сильным движениям на валютном рынке. Доводилось ли вам следить за выходом отчетов по количеству рабочих мест вне аграрного сектора в США (Non-Farm Payrolls)? Если да, то вы знаете, что выход этих отчетов часто определяет последующее движение валют и может ускорить смену тренда.
Рис. 1. Газеты. Изображение распространяется по лицензии Creative Commons
1. Приступаем к программированию нашего торгового советника
1.1. Торговая идея
Торговая идея для нашей системы уже обсуждалась выше. Идея заманчива, но как реализовать ее в программном коде? Мы будем опираться главным образом на два аспекта языка MQL5. Во-первых, мы будем использовать индикатор Momentum, чтобы определить величину воздействия того или иного набора новостей на валютную пару. Во-вторых, нам нужно будет поработать с файловыми функциями языка MQL5, чтобы добиться хранения нашего новостного календаря в файловой системе. Используемый формат файлов – CSV. Мы будем создавать данного робота в соответствии с объектно-ориентированной парадигмой, применяя концептуальный подход, описанный в статье Приобщаемся к объектно-ориентированному программированию в MQL5. Наш объектно-ориентированный подход предусматривает загрузку CSV-файлов в память компьютера. Таким образом торговый советник сможет принимать решения на основе полученных данных.
1.2. Основа объектно-ориентированного робота
Здесь и далее мы будем рассматривать нашего торгового советника, как если бы это было живое существо. Как вы помните, мы теперь следуем идеологии ООП. Используя такой подход, мы можем поделить нашего советника на несколько частей, таких как мозг, так называемая эволюция, набор индикаторов и набор новостей. Я поясню все это ниже.
//+---------------------------------------------------------------------+ //| ExpertNewsWatcher.mq5 | //| Copyright © 2013, laplacianlab, Jordi Bassagañas | //+---------------------------------------------------------------------+ #property copyright "Copyright © 2013, laplacianlab. Jordi Bassagañas" #property link "https://www.mql5.com/ru/articles" #property version "1.00" #property tester_file "news_watcher.csv" #include <..\Experts\NewsWatcher\CNewsWatcher.mqh> input ENUM_TIMEFRAMES Period=PERIOD_M1; input int StopLoss=400; input int TakeProfit=600; input double LotSize=0.01; input string CsvFile="news_watcher.csv"; MqlTick tick; CNewsWatcher* NW = new CNewsWatcher(StopLoss,TakeProfit,LotSize,CsvFile); int OnInit(void) { NW.Init(); NW.GetTechIndicators().GetMomentum().SetHandler(Symbol(), Period, 13, PRICE_CLOSE); return(0); } void OnDeinit(const int reason) { delete(NW); } void OnTick() { SymbolInfoTick(_Symbol, tick); NW.GetTechIndicators().GetMomentum().UpdateBuffer(2); NW.OnTick(tick.ask,tick.bid); } //+------------------------------------------------------------------+
CNewsWatcher – основной класс советника. Давайте посмотрим на код.
//+---------------------------------------------------------------------+ //| CNewsWatcher.mqh | //| Copyright © 2013, Jordi Bassagañas | //+---------------------------------------------------------------------+ #include <Trade\Trade.mqh> #include <Mine\Enums.mqh> #include <..\Experts\NewsWatcher\CBrain.mqh> #include <..\Experts\NewsWatcher\CEvolution.mqh> #include <..\Experts\NewsWatcher\CTechIndicators.mqh> //+---------------------------------------------------------------------+ //| Класс CNewsWatcher | //+---------------------------------------------------------------------+ class CNewsWatcher { protected: //--- Пользовательские типы CBrain *m_brain; CEvolution *m_evolution; CTechIndicators *m_techIndicators; //--- Типы MQL5 CTrade *m_trade; CPositionInfo *m_positionInfo; public: //--- Конструктор и деструктор CNewsWatcher(int stop_loss,int take_profit,double lot_size,string csv_file); ~CNewsWatcher(void); //--- Геттеры CBrain *GetBrain(void); CEvolution *GetEvolution(void); CTechIndicators *GetTechIndicators(void); CTrade *GetTrade(void); CPositionInfo *GetPositionInfo(void); //--- Методы CNewsWatcher bool Init(); void Deinit(void); void OnTick(double ask,double bid); }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CNewsWatcher::CNewsWatcher(int stop_loss,int take_profit,double lot_size, string csv_file) { m_brain=new CBrain(stop_loss,take_profit,lot_size,csv_file); m_evolution=new CEvolution(DO_NOTHING); m_techIndicators=new CTechIndicators; m_trade=new CTrade(); } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CNewsWatcher::~CNewsWatcher(void) { Deinit(); } //+------------------------------------------------------------------+ //| GetBrain | //+------------------------------------------------------------------+ CBrain *CNewsWatcher::GetBrain(void) { return m_brain; } //+------------------------------------------------------------------+ //| GetEvolution | //+------------------------------------------------------------------+ CEvolution *CNewsWatcher::GetEvolution(void) { return m_evolution; } //+------------------------------------------------------------------+ //| GetTechIndicators | //+------------------------------------------------------------------+ CTechIndicators *CNewsWatcher::GetTechIndicators(void) { return m_techIndicators; } //+------------------------------------------------------------------+ //| GetTrade | //+------------------------------------------------------------------+ CTrade *CNewsWatcher::GetTrade(void) { return m_trade; } //+------------------------------------------------------------------+ //| GetPositionInfo | //+------------------------------------------------------------------+ CPositionInfo *CNewsWatcher::GetPositionInfo(void) { return m_positionInfo; } //+------------------------------------------------------------------------+ //| CNewsWatcher OnTick | //| Проверяем характер ценового движения в момент выхода новости | //+------------------------------------------------------------------------+ void CNewsWatcher::OnTick(double ask,double bid) { //--- имеются ли какие-либо новости для обработки? if(GetBrain().GetNewsContainer().GetCurrentIndex() < GetBrain().GetNewsContainer().GetTotal()) { double momentumBuffer[]; GetTechIndicators().GetMomentum().GetBuffer(momentumBuffer, 2); //--- Количество секунд перед выходом новостей. GMT +- timeWindow – фактическое время, с которого робот начинает //--- следить за рынком. Например, если выход новостей ожидается в 13:00 по Гринвичу, вы можете установить TimeWindow //--- на 900 секунд. Таким образом, советник начинает следить за рынком за пятнадцать минут до выхода новостей. int timeWindow=600; CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew(); int indexCurrentNew = GetBrain().GetNewsContainer().GetCurrentIndex(); if(TimeGMT() >= currentNew.GetTimeRelease() + timeWindow) { GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+1); return; } //--- есть ли открытая позиция? if(!m_positionInfo.Select(_Symbol)) { //--- если открытой позиции нет, открываем ее bool timeHasCome = TimeGMT() >= currentNew.GetTimeRelease() - timeWindow && TimeGMT() <= currentNew.GetTimeRelease() + timeWindow; if(timeHasCome && momentumBuffer[0] > 100.10) { GetEvolution().SetStatus(SELL); GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+1); } else if(timeHasCome && momentumBuffer[0] < 99.90) { GetEvolution().SetStatus(BUY); GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+1); } } //--- если есть открытая позиция, ждем отработки мат. ожидания else { GetEvolution().SetStatus(DO_NOTHING); } double tp; double sl; switch(GetEvolution().GetStatus()) { case BUY: tp = ask + m_brain.GetTakeProfit() * _Point; sl = bid - m_brain.GetStopLoss() * _Point; GetTrade().PositionOpen(_Symbol,ORDER_TYPE_BUY,m_brain.GetSize(),ask,sl,tp); break; case SELL: sl = ask + m_brain.GetStopLoss() * _Point; tp = bid - m_brain.GetTakeProfit() * _Point; GetTrade().PositionOpen(_Symbol,ORDER_TYPE_SELL,m_brain.GetSize(),bid,sl,tp); break; case DO_NOTHING: // Ничего не происходит... break; } } //--- выходим, как только все новости в контейнере обработаны else return; } //+------------------------------------------------------------------+ //| Инициализация CNewsWatcher | //+------------------------------------------------------------------+ bool CNewsWatcher::Init(void) { // Здесь располагается логика инициализации... return true; } //+------------------------------------------------------------------+ //| Деинициализация CNewsWatcher | //+------------------------------------------------------------------+ void CNewsWatcher::Deinit(void) { delete(m_brain); delete(m_evolution); delete(m_techIndicators); delete(m_trade); Print("Деинициализация CNewsWatcher выполнена!"); Print("Спасибо, что воспользовались данным советником."); } //+------------------------------------------------------------------+
Не беспокойтесь, если вам что-то на данном этапе непонятно. Это нормально. Во-первых, вам необходимо изучить все элементы данного эксперта, чтобы понять, как все работает. Я рекомендую вам сначала прочитать эту статью поверхностно, а затем перечитать ее еще один-два раза, вникая в подробности. В любом случае, попытаюсь объяснить ключевые элементы CNewsWatcher.
Самая важная часть советника – это, конечно же, метод OnTick, в котором, как вы увидите, CNewsWatcher использует для работы объектно-ориентированный новостной контейнер. Данный элемент, который может рассматриваться как обычная газета, содержит новости, предназначенные для советника.
Обратите внимание на то, как мы получаем новостной контейнер:
GetBrain().GetNewsContainer();
А также на то, как мы получаем последние новости для обработки:
CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew();
Это делается с помощью CBrain. Помните, что CBrain – важное центральное звено в нашем объектно-ориентированном подходе, содержащее элементы для нормальной работы советника. Другими словами, это что-то вроде постоянного запоминающего устройства (ПЗУ).
//+------------------------------------------------------------------+ //| CBrain.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <..\Experts\NewsWatcher\CNewsContainer.mqh> //+------------------------------------------------------------------+ //| Класс CBrain | //+------------------------------------------------------------------+ class CBrain { protected: double m_size; // Размер позиций int m_stopLoss; // Stop loss int m_takeProfit; // Take profit CNewsContainer *m_news_container; // Новостной контейнер public: //--- Конструктор и деструктор CBrain(int stopLoss,int takeProfit,double size,string csv); ~CBrain(void); //--- Геттеры double GetSize(void); int GetStopLoss(void); int GetTakeProfit(void); CNewsContainer *GetNewsContainer(void); //--- Сеттеры void SetSize(double size); void SetStopLoss(int stopLoss); void SetTakeProfit(int takeProfit); //--- Особые методы CBrain bool Init(); void Deinit(void); }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CBrain::CBrain(int stopLoss,int takeProfit,double size,string csv) { m_size=size; m_stopLoss=stopLoss; m_takeProfit=takeProfit; m_news_container=new CNewsContainer(csv); } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CBrain::~CBrain(void) { Deinit(); } //+------------------------------------------------------------------+ //| GetSize | //+------------------------------------------------------------------+ double CBrain::GetSize(void) { return m_size; } //+------------------------------------------------------------------+ //| GetStopLoss | //+------------------------------------------------------------------+ int CBrain::GetStopLoss(void) { return m_stopLoss; } //+------------------------------------------------------------------+ //| GetTakeProfit | //+------------------------------------------------------------------+ int CBrain::GetTakeProfit(void) { return m_takeProfit; } //+------------------------------------------------------------------+ //| GetNewsContainer | //+------------------------------------------------------------------+ CNewsContainer *CBrain::GetNewsContainer(void) { return m_news_container; } //+------------------------------------------------------------------+ //| SetSize | //+------------------------------------------------------------------+ void CBrain::SetSize(double size) { m_size=size; } //+------------------------------------------------------------------+ //| SetStopLoss | //+------------------------------------------------------------------+ void CBrain::SetStopLoss(int stopLoss) { m_stopLoss=stopLoss; } //+------------------------------------------------------------------+ //| SetTakeProfit | //+------------------------------------------------------------------+ void CBrain::SetTakeProfit(int takeProfit) { m_takeProfit=takeProfit; } //+------------------------------------------------------------------+ //| Инициализация CBrain | //+------------------------------------------------------------------+ bool CBrain::Init(void) { // Здесь располагается логика инициализации... return true; } //+------------------------------------------------------------------+ //| Деинициализация CBrain | //+------------------------------------------------------------------+ void CBrain::Deinit(void) { delete(m_news_container); Print("Деинициализация CBrain выполнена!"); } //+------------------------------------------------------------------+
CNewsWatcher последовательно читает новости, содержащиеся в контейнере (газете). Если в этот момент на рынке наблюдается сильное ценовое движение, советник открывает позицию.
Торговый робот запрограммирован на противодействующую стратегию. Например, если на рынке наблюдается рост цен, советник исходит из того, что цена скоро упадет, и поэтому открывает короткую позицию. Соответственно, при сильном падении советник открывает длинную позицию. Разумеется, это поведение может быть усложнено. Ограниченный объем статьи не позволяет мне описать создание высокоэффективного новостного советника. Как я уже говорил, основная цель статьи – снабдить вас техническими основами, которые вы могли бы применить в своих будущих разработках.
Рис. 2. Робот на кормовом поручне. Изображение распространяется по лицензии Creative Commons
Продолжая придерживаться используемых понятий, было бы интересно создать объектно-ориентированную основу технических индикаторов в рамках нового подхода. Таким образом, эта часть головоломки наилучшим образом подходит ко всему. Предположим, что на данном этапе разработки мы создадим некий объектно-ориентированный фреймворк для более комфортной работы с элементами MQL5, которые изначально не слишком подходят для объектно-ориентированного подхода.
На этом этапе мы можем обратиться к Стандартной библиотеке MQL5. Эта библиотека упрощает разработку программ (индикаторов, скриптов, советников), предоставляя удобный доступ к большинству внутренних функций MQL5. В нашем сегодняшнем упражнении мы отчасти используем функционал Стандартной библиотеки. Как уже было сказано, она весьма удобна с точки зрения ООП. Яркий пример – новостной контейнер, о котором я еще скажу позже. Мы используем в нем MQL5-класс CArrayObj , чтобы хранить в оперативной памяти компьютера наши пользовательские объектно-ориентированные новости сложного типа.
Чтобы получить больше информации по теме, ознакомьтесь со Стандартной библиотекой, которая содержит некоторые классы для работы с индикаторами. В этой статье на нескольких простых учебных примерах рассматривается работа с объектно-ориентированными элементами.
//+------------------------------------------------------------------+ //| CTechIndicators.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <..\Experts\NewsWatcher\CMomentum.mqh> //+------------------------------------------------------------------+ //| Класс CTechIndicators | //+------------------------------------------------------------------+ class CTechIndicators { protected: CMomentum *m_momentum; public: //--- Конструктор и деструктор CTechIndicators(void); ~CTechIndicators(void); //--- Геттеры CMomentum *GetMomentum(void); //--- Особые методы CTechIndicators bool Init(); void Deinit(void); }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CTechIndicators::CTechIndicators(void) { m_momentum = new CMomentum; } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CTechIndicators::~CTechIndicators(void) { Deinit(); } //+------------------------------------------------------------------+ //| GetMomentum | //+------------------------------------------------------------------+ CMomentum* CTechIndicators::GetMomentum(void) { return m_momentum; } //+------------------------------------------------------------------+ //| Инициализация CTechIndicators | //+------------------------------------------------------------------+ bool CTechIndicators::Init(void) { // Здесь располагается логика инициализации... return true; } //+------------------------------------------------------------------+ //| Деинициализация CTechIndicators | //+------------------------------------------------------------------+ void CTechIndicators::Deinit(void) { delete(m_momentum); Print("Деинициализация CTechIndicators выполнена!"); } //+------------------------------------------------------------------+1.3.2. CMomentum, объектно-ориентированная основа для iMomentum
//+------------------------------------------------------------------+ //| CMomentum.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Класс CMomentum | //+------------------------------------------------------------------+ class CMomentum { protected: int m_handler; double m_buffer[]; public: //--- Конструктор и деструктор CMomentum(void); ~CMomentum(void); //--- Геттеры int GetHandler(void); void GetBuffer(double &buffer[], int ammount); //--- Сеттеры bool SetHandler(string symbol,ENUM_TIMEFRAMES period,int mom_period,ENUM_APPLIED_PRICE mom_applied_price); bool UpdateBuffer(int ammount); }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CMomentum::CMomentum(void) { ArraySetAsSeries(m_buffer, true); } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CMomentum::~CMomentum(void) { IndicatorRelease(m_handler); ArrayFree(m_buffer); } //+------------------------------------------------------------------+ //| GetHandler | //+------------------------------------------------------------------+ int CMomentum::GetHandler(void) { return m_handler; } //+------------------------------------------------------------------+ //| GetBuffer | //+------------------------------------------------------------------+ void CMomentum::GetBuffer(double &buffer[], int ammount) { ArrayCopy(buffer, m_buffer, 0, 0, ammount); } //+------------------------------------------------------------------+ //| SetHandler | //+------------------------------------------------------------------+ bool CMomentum::SetHandler(string symbol,ENUM_TIMEFRAMES period,int mom_period,ENUM_APPLIED_PRICE mom_applied_price) { if((m_handler=iMomentum(symbol,period,mom_period,mom_applied_price))==INVALID_HANDLE) { printf("Ошибка при создании индикатора Momentum"); return false; } return true; } //+------------------------------------------------------------------+ //| UpdateBuffer | //+------------------------------------------------------------------+ bool CMomentum::UpdateBuffer(int ammount) { if(CopyBuffer(m_handler, 0, 0, ammount, m_buffer) < 0) { Alert("Ошибка при копировании буферов Momentum, ошибка: " , GetLastError()); return false; } return true; } //+------------------------------------------------------------------+
1.4. Объектно-ориентированный новостной контейнер
Короткие новостные заметки – ключевой элемент, с которым нашему советнику придется иметь дело. При заключении новости в объектно-ориентированный контейнер можно использовать аналогию с газетой. Проще говоря, объектно-ориентированный контейнер CNewsContainer и есть газета. Сами новости при этом названы CNew. Таков наш пользовательский объектно-ориентированный тип для представления новостей.
1.4.1. Новостной контейнер CNewsContainer
//+------------------------------------------------------------------+ //| CNewsContainer.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <Files\FileTxt.mqh> #include <Arrays\ArrayObj.mqh> #include <..\Experts\NewsWatcher\CNew.mqh> //+------------------------------------------------------------------+ //| Класс CNewsContainer | //+------------------------------------------------------------------+ class CNewsContainer { protected: string m_csv; // Имя csv-файла CFileTxt m_fileTxt; // Функционал файла MQL5 int m_currentIndex; // Индекс следующих новостей, предназначенных для обработки в контейнере int m_total; // Общее число новостей, предназначенных для обработки CArrayObj *m_news; // Список новостей в памяти компьютера, загруженный из csv-файла public: //--- Конструктор и деструктор CNewsContainer(string csv); ~CNewsContainer(void); //--- Геттеры int GetCurrentIndex(void); int GetTotal(void); CNew *GetCurrentNew(); CArrayObj *GetNews(void); //--- Сеттеры void SetCurrentIndex(int index); void SetTotal(int total); void SetNews(void); //--- Методы CNewsContainer bool Init(); void Deinit(void); }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CNewsContainer::CNewsContainer(string csv) { m_csv=csv; m_news=new CArrayObj; SetNews(); } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CNewsContainer::~CNewsContainer(void) { Deinit(); } //+------------------------------------------------------------------+ //| GetCurrentIndex | //+------------------------------------------------------------------+ int CNewsContainer::GetCurrentIndex(void) { return m_currentIndex; } //+------------------------------------------------------------------+ //| GetTotal | //+------------------------------------------------------------------+ int CNewsContainer::GetTotal(void) { return m_total; } //+------------------------------------------------------------------+ //| GetNews | //+------------------------------------------------------------------+ CArrayObj *CNewsContainer::GetNews(void) { return m_news; } //+------------------------------------------------------------------+ //| GetCurrentNew | //+------------------------------------------------------------------+ CNew *CNewsContainer::GetCurrentNew(void) { return m_news.At(m_currentIndex); } //+------------------------------------------------------------------+ //| SetCurrentIndex | //+------------------------------------------------------------------+ void CNewsContainer::SetCurrentIndex(int index) { m_currentIndex=index; } //+------------------------------------------------------------------+ //| SetTotal | //+------------------------------------------------------------------+ void CNewsContainer::SetTotal(int total) { m_total=total; } //+------------------------------------------------------------------+ //| SetNews | //+------------------------------------------------------------------+ void CNewsContainer::SetNews(void) { //--- прежде всего инициализируем некоторые переменные! SetCurrentIndex(0); string sep= ";"; ushort u_sep; string substrings[]; u_sep=StringGetCharacter(sep,0); //--- затем откроем и обработаем CSV-файл int file_handle=m_fileTxt.Open(m_csv, FILE_READ|FILE_CSV); if(file_handle!=INVALID_HANDLE) { while(!FileIsEnding(file_handle)) { string line = FileReadString(file_handle); int k = StringSplit(line,u_sep,substrings); CNew *current = new CNew(substrings[0],(datetime)substrings[1],substrings[2]); m_news.Add(current); } FileClose(file_handle); //--- и наконец обработаем и подсчитаем новости m_news.Delete(0); // --- здесь мы удаляем заголовок CSV! SetTotal(m_news.Total()); } else { Print("Не удалось открыть файл ",m_csv); Print("Код ошибки ",GetLastError()); } } //+------------------------------------------------------------------+ //| Инициализация CNewsContainer | //+------------------------------------------------------------------+ bool CNewsContainer::Init(void) { // Здесь располагается логика инициализации... return true; } //+------------------------------------------------------------------+ //| Деинициализация CNewsContainer | //+------------------------------------------------------------------+ void CNewsContainer::Deinit(void) { m_news.DeleteRange(0, m_total-1); delete(m_news); Print("Деинициализация CNewsContainer выполнена!"); } //+------------------------------------------------------------------+
SetNews является наиболее важным методом CNewsContainer. Данный метод считывает CSV-файл и загружает его в оперативную память компьютера в виде объектов типа CNew. Кстати, я до сих пор не упомянул о том, что CSV-файлы должны храниться в data_folder\MQL5\FILES\. Пожалуйста, ознакомьтесь с файловыми операциями для более глубокого понимания функций, используемых в SetNews.
1.4.2. CNew, новости
//+------------------------------------------------------------------+ //| CNew.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <Object.mqh> //+------------------------------------------------------------------+ //| Класс CNew | //+------------------------------------------------------------------+ class CNew : public CObject { protected: string m_country; // Страна datetime m_time_release; // Дата и время выхода новости string m_name; // Название новости public: //--- Конструктор и деструктор CNew(string country,datetime time_release,string name); ~CNew(void); //--- Геттеры string GetCountry(void); datetime GetTimeRelease(void); string GetName(void); //--- Сеттеры void SetCountry(string country); void SetTimeRelease(datetime time_release); void SetName(string name); //--- Особые методы CNew bool Init(); void Deinit(void); }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CNew::CNew(string country,datetime time_release,string name) { m_country=country; m_time_release=time_release; m_name=name; } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CNew::~CNew(void) { Deinit(); } //+------------------------------------------------------------------+ //| GetCountry | //+------------------------------------------------------------------+ string CNew::GetCountry(void) { return m_country; } //+------------------------------------------------------------------+ //| GetTimeRelease | //+------------------------------------------------------------------+ datetime CNew::GetTimeRelease(void) { return m_time_release; } //+------------------------------------------------------------------+ //| GetName | //+------------------------------------------------------------------+ string CNew::GetName(void) { return m_name; } //+------------------------------------------------------------------+ //| SetCountry | //+------------------------------------------------------------------+ void CNew::SetCountry(string country) { m_country=country; } //+------------------------------------------------------------------+ //| SetTimeRelease | //+------------------------------------------------------------------+ void CNew::SetTimeRelease(datetime timeRelease) { m_time_release=timeRelease; } //+------------------------------------------------------------------+ //| SetName | //+------------------------------------------------------------------+ void CNew::SetName(string name) { m_name=name; } //+------------------------------------------------------------------+ //| Инициализация CNew | //+------------------------------------------------------------------+ bool CNew::Init(void) { //--- Здесь располагается логика инициализации... return true; } //+------------------------------------------------------------------+ //| Деинициализация CNew | //+------------------------------------------------------------------+ void CNew::Deinit(void) { //--- Здесь располагается логика деинициализации... Print("Деинициализация CNew выполнена!"); } //+------------------------------------------------------------------+
2. Тестирование ExpertNewsWatcher.mq5 на истории
2.1. Приложения
ExpertNewsWatcher состоит из следующих файлов:
- Enums.mqh
- CBrain.mqh
- CEvolution.mqh
- CMomentum.mqh
- CNew.mqh
- CNewsContainer.mqh
- CNewsWatcher.mqh
- CTechIndicators.mqh
- ExpertNewsWatcher.mq5
- news_watcher.txt
2.2. Установка
Прежде всего необходимо создать папку MQL5\Include\Mine для хранения пользовательских элементов, а затем скопировать туда файл Enums.mqh. Сразу после этого необходимо создать папку MQL5\Experts\NewsWatcher и скопировать туда файлы, указанные ниже:
- CBrain.mqh
- CEvolution.mqh
- CMomentum.mqh
- CNew.mqh
- CNewsContainer.mqh
- CNewsWatcher.mqh
- CTechIndicators.mqh
- ExpertNewsWatcher.mq5
Внимание! После перечисленных действий переименуйте файл news_watcher.txt, в news_watcher.csv и положите его в папку data_folder\MQL5\FILES\. На момент публикации присоединение csv-файлов в качестве приложений к статье невозможно, но можно прикладывать txt-файлы.
Не забывайте о компиляции. Теперь вы можете протестировать ExpertNewsWatcher на истории, как любой другой советник.
2.3. Результаты тестирования на истории
ExpertNewsWatcher запускался со следующими входными параметрами.
- Period = 1 Minute
- StopLoss = 400
- TakeProfit = 600
- LotSize = 0.01
- CsvFile = news_watcher.csv
Изначально я использовал следующие фиктивные данные, содержащие набор вымышленных новостей, распределенных во времени, чтобы увидеть, как робот ведет себя в регулируемых условиях. Данные периоды удовлетворяют установленным исходным условиям, то есть в эти моменты времени ценовое движение достаточно велико для открытия позиции на покупку или продажу. Вы можете использовать данные входы в рынок для тестирования.
Примеры фиктивных данных для использования в news_watcher.csv:
Country;Time;Event USD;2013.06.03 17:19:00;A. Momentum equals 100.47 USD;2013.06.13 17:09:00;B. Momentum equals 100.40 USD;2013.06.21 18:52:00;C. Momentum equals 100.19 USD;2013.07.01 17:32:00;D. Momentum equals 100.18 USD;2013.07.08 15:17:00;E. Momentum equals 100.18 USD;2013.07.16 10:00:00;F. Momentum equals 99.81 USD;2013.07.24 09:30:00;G. Momentum equals 100.25
Рис. 3. Результаты, полученные при использовании фиктивных данных
Указанный график с фиктивными новостями дает представление о том, как данный робот может вести себя в реальных условиях. Поместите следующие реальные данные, взятые на DailyFX, в news_watcher.csv и снова запустите ExpertNewsWatcher.
Примеры реальных данных для использования в news_watcher.csv:
Country;Time;Event USD;2013.07.15 12:00:00;USD Fed's Tarullo Speaks on Banking Regulation in Washington USD;2013.07.15 12:30:00;USD Advance Retail Sales (JUN) and others USD;2013.07.15 14:00:00;USD USD Business Inventories (MAY) USD;2013.07.15 21:00:00;USD EIA Gasoline and Diesel Fuel Update USD;2013.07.16 12:30:00;USD Several Consumer Price Indexes USD;2013.07.16 13:00:00;USD USD Net Long-term TIC Flows (MAY) & USD Total Net TIC Flows (MAY) USD;2013.07.16 13:15:00;USD Industrial Production (JUN) and others USD;2013.07.16 14:00:00;USD NAHB Housing Market Index (JUL) USD;2013.07.16 18:15:00;USD Fed's George Speaks on Economic Conditions and Agriculture USD;2013.07.22 12:30:00;USD Chicago Fed Nat Activity Index (JUN) USD;2013.07.22 14:00:00;USD Existing Home Sales (MoM) (JUN) & Existing Home Sales (JUN) USD;2013.07.22 21:00:00;USD EIA Gasoline and Diesel Fuel Update USD;2013.07.23 13:00:00;USD House Price Index (MoM) (MAY) USD;2013.07.23 14:00:00;USD Richmond Fed Manufacturing Index (JUL) USD;2013.07.24 11:00:00;USD MBA Mortgage Applications (JUL 19) USD;2013.07.24 12:58:00;USD Markit US PMI Preliminary (JUL) USD;2013.07.24 14:00:00;USD USD New Home Sales (MoM) (JUN) & USD New Home Sales (JUN) USD;2013.07.24 14:30:00;USD USD DOE U.S. Crude Oil Inventories (JUL 19) and others
Рис. 4. Результаты, полученные при использовании реальных данных
Этот простой обработчик новостей может работать одновременно только с одной новостью, выходящей в определенное время. По этой причине конкретное время, например, 2013.07.15 12:30:00, может содержать несколько новостей. Если в конкретный момент времени выходят несколько важных новостей, сделайте одну запись в CSV-файле.
Учтите, что торговый советник проводит только три операции на рынке при работе с реальными данными. Причина в том, что, в отличие от набора фиктивных новостей, разделенных во времени, в реальности некоторые новости могут выходить одновременно. Наш торговый робот прежде всего завершает первую операцию в последовательности, игнорируя выходящую новость в случае, когда открытая позиция уже имеется.
double momentumBuffer[]; GetTechIndicators().GetMomentum().GetBuffer(momentumBuffer, 2); //--- Количество секунд перед выходом новостей. GMT +- timeWindow – фактическое время, с которого робот начинает //--- следить за рынком. Например, если выход новостей ожидается в 13:00 по Гринвичу, вы можете установить TimeWindow //--- на 900 секунд. Таким образом, советник начинает следить за рынком за пятнадцать минут до выхода новостей. int timeWindow=600; CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew(); int indexCurrentNew = GetBrain().GetNewsContainer().GetCurrentIndex(); if(TimeGMT() >= currentNew.GetTimeRelease() + timeWindow) { GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+1); return; } //--- имеется ли открытая позиция? if(!m_positionInfo.Select(_Symbol)) { //--- если открытой позиции нет, открываем ее bool timeHasCome = TimeGMT() >= currentNew.GetTimeRelease() - timeWindow && TimeGMT() <= currentNew.GetTimeRelease() + timeWindow; if(timeHasCome && momentumBuffer[0] > 100.10) { GetEvolution().SetStatus(SELL); GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+1); } else if(timeHasCome && momentumBuffer[0] < 99.90) { GetEvolution().SetStatus(BUY); GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+1); } } //--- если имеется открытая позиция, ждем отработки мат. ожидания else { GetEvolution().SetStatus(DO_NOTHING); }
Заключение
Это было продолжение статьи "Приобщаемся к объектно-ориентированному программированию в MQL5", в которой рассматривалось создание с нуля простого объектно-ориентированного советника, а также были даны некоторые советы по ООП. Продолжая начатую тему, настоящая статья предоставила вам инструменты, необходимые для разработки вашего собственного новостного советника. Мы рассмотрели реализацию объектно-ориентированных контейнеров и основ для комфортной работы в соответствии с объектно-ориентированной парадигмой. Мы также обсудили Стандартную библиотеку и функции MQL5 для работы с файловой системой.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/719





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