Building an Automatic News Trader
Introduction
As Investopedia states, a news trader is "a trader or investor who makes trading or investing decisions based on news announcements". Indeed, economic reports such as a country's GDP, consumer confidence indexes and employment data of countries, amongst others, often produce significant movements in the currency markets. Have you ever attended a U.S. Non-Farm Payrolls release? If so, you already know that these reports may determine currencies' recent future and act as catalysts for trends reversals.
Figure 1. Newspapers B&W. Image distributed under a Creative Commons License on Flickr
1. Let's Program Our EA
1.1. The Trading System's Idea
The idea behind this system is what we have discussed above. This sounds great, but how can we implement that proven fact in the programming world? Mainly we are relying on two MQL5 aspects. On the one hand, we are using Momentum indicator to measure the impact of the given set of news in a currency pair. On the other hand, we will work with MQL5 file functions to store our favorite news calendar in the filesystem. The chosen file format is CSV. We are going to program this robot under the object-oriented paradigm, of course, with the conceptual approach discussed in Another MQL5 OOP class. Our object-oriented design will load the CSV into the computer's memory so that the EA can make decisions based on such information.
1.2. The Robot's OOP Skeleton
From now on we are conceiving our EAs from the point of view of concepts, as if they were living creatures. We are now OOP people, do you remember? Thanks to this vision we can compose our Expert Advisor of several parts such as a brain, something that we call evolution, a set of indicators and a set of news. I will clarify all this below.
//+---------------------------------------------------------------------+ //| ExpertNewsWatcher.mq5 | //| Copyright © 2013, laplacianlab, Jordi Bassagañas | //+---------------------------------------------------------------------+ #property copyright "Copyright © 2013, laplacianlab. Jordi Bassagañas" #property link "https://www.mql5.com/en/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 is the EA's main class. Let's have a look at the code.
//+---------------------------------------------------------------------+ //| 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 | //+---------------------------------------------------------------------+ class CNewsWatcher { protected: //--- Custom types CBrain *m_brain; CEvolution *m_evolution; CTechIndicators *m_techIndicators; //--- MQL5 types CTrade *m_trade; CPositionInfo *m_positionInfo; public: //--- Constructor and destructor methods CNewsWatcher(int stop_loss,int take_profit,double lot_size,string csv_file); ~CNewsWatcher(void); //--- Getter methods CBrain *GetBrain(void); CEvolution *GetEvolution(void); CTechIndicators *GetTechIndicators(void); CTrade *GetTrade(void); CPositionInfo *GetPositionInfo(void); //--- CNewsWatcher methods bool Init(); void Deinit(void); void OnTick(double ask,double bid); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ 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(); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ 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 | //| Checks momentum's turbulences around the time of the news release | //+------------------------------------------------------------------------+ void CNewsWatcher::OnTick(double ask,double bid) { //--- are there some news to process? if(GetBrain().GetNewsContainer().GetCurrentIndex() < GetBrain().GetNewsContainer().GetTotal()) { double momentumBuffer[]; GetTechIndicators().GetMomentum().GetBuffer(momentumBuffer, 2); //--- Number of seconds before the news releases. GMT +- timeWindow is the real time from which the robot starts //--- listening to the market. For instance, if there is a news release programmed at 13:00 GMT you can set TimeWindow //--- to 900 seconds so that the EA starts listening to the market fifteen minutes before that news release. int timeWindow=600; CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew(); int indexCurrentNew = GetBrain().GetNewsContainer().GetCurrentIndex(); if(TimeGMT() >= currentNew.GetTimeRelease() + timeWindow) { GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+1); return; } //--- is there any open position? if(!m_positionInfo.Select(_Symbol)) { //--- if there is no open position, we try to open one 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); } } //--- if there is an open position, we let it work the mathematical expectation 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: // Nothing... break; } } //--- we exit when all the container's news have been processed else return; } //+------------------------------------------------------------------+ //| CNewsWatcher initialization | //+------------------------------------------------------------------+ bool CNewsWatcher::Init(void) { // Initialization logic here... return true; } //+------------------------------------------------------------------+ //| CNewsWatcher deinitialization | //+------------------------------------------------------------------+ void CNewsWatcher::Deinit(void) { delete(m_brain); delete(m_evolution); delete(m_techIndicators); delete(m_trade); Print("CNewsWatcher deinitialization performed!"); Print("Thank you for using this EA."); } //+------------------------------------------------------------------+
For now, do not worry if you do not see things very clear, it is normal. First, you have to study all the parts of this Expert Advisor to understand how everything works. I recommend you first read superficially this article and then make a second and third deeper readings. Anyway, I will try to explain at this time some key parts of CNewsWatcher.
The EA's most important part is of course the method OnTick where you will see that CNewsWatcher uses an OO news container to work. This piece, which can be seen as a real-world newspaper, contains the news that the EA user wants to trade.
Note that we retrieve the news container like this:
GetBrain().GetNewsContainer();
And we retrieve the current news to be processed this way:
CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew();
This is done through CBrain. Remember that CBrain is an important central point in our object-oriented design containing those things needed so that the EA can properly operate, it is something like a read-only memory (ROM).
//+------------------------------------------------------------------+ //| CBrain.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <..\Experts\NewsWatcher\CNewsContainer.mqh> //+------------------------------------------------------------------+ //| CBrain Class | //+------------------------------------------------------------------+ class CBrain { protected: double m_size; // The size of the positions int m_stopLoss; // Stop loss int m_takeProfit; // Take profit CNewsContainer *m_news_container; // The news container public: //--- Constructor and destructor methods CBrain(int stopLoss,int takeProfit,double size,string csv); ~CBrain(void); //--- Getter methods double GetSize(void); int GetStopLoss(void); int GetTakeProfit(void); CNewsContainer *GetNewsContainer(void); //--- Setter methods void SetSize(double size); void SetStopLoss(int stopLoss); void SetTakeProfit(int takeProfit); //--- CBrain specific methods bool Init(); void Deinit(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ 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); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ 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 initialization | //+------------------------------------------------------------------+ bool CBrain::Init(void) { // Initialization logic here... return true; } //+------------------------------------------------------------------+ //| CBrain deinitialization | //+------------------------------------------------------------------+ void CBrain::Deinit(void) { delete(m_news_container); Print("CBrain deinitialization performed!"); } //+------------------------------------------------------------------+
CNewsWatcher is basically reading one by one the news stored in the container (the newspaper). If at that time there is a strong acceleration in the price then it places an order in the market.
Regarding the purchasing or selling of lots the robot is programmed in a reactive way. Let's say, when a strong upward movement occurs, the EA assumes that the price will retract and therefore sells. Similarly, when there is a strong downward movement the robot places a long position in the market thinking that the price will retrace in short. This can be improved, of course, in this article there is no space enough to develop a highly efficient automatic news trader, as said before, the goal is to give you the technical basics in order for you to continue advancing in your own developments.
Figure 2. Robot on the Taff. Image distributed under a Creative Commons License on Flickr
Once again, as we have decided to address our apps from the perspective of concepts, it is interesting to program our own object-oriented wrappers for technical indicators to adhere the new paradigm. Thus this piece of the puzzle fits much better with everything. Let's say that in this part of our development we take advantage to build something like an object-oriented framework in order for us to work more comfortably with that MQL5 stuff not very OO out of the box.
At this point, it is interesting to note that there is the MQL5 Standard Library. This library is designed to facilitate writing programs (indicators, scripts, experts) to end users, providing convenient access to most of MQL5 internal functions. In fact, in today's exercise we are using some Standard Library's functionality because, as it's been said, it is much more comfortable from the point of view of OO programming. A clear example is the news container that I'll explain a little later, we will use MQL5 class CArrayObj there to store in the computer's RAM our custom object-oriented news of complex type.
Please, take a look at the official documentation entitled Standard Library to learn more about this topic and note that the Standard Library already comes with some classes to work with indicators. This article discusses the need to work with object-oriented material through some simple examples for teaching purposes.
//+------------------------------------------------------------------+ //| CTechIndicators.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <..\Experts\NewsWatcher\CMomentum.mqh> //+------------------------------------------------------------------+ //| CTechIndicators Class | //+------------------------------------------------------------------+ class CTechIndicators { protected: CMomentum *m_momentum; public: //--- Constructor and destructor methods CTechIndicators(void); ~CTechIndicators(void); //--- Getter methods CMomentum *GetMomentum(void); //--- CTechIndicators specific methods bool Init(); void Deinit(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CTechIndicators::CTechIndicators(void) { m_momentum = new CMomentum; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CTechIndicators::~CTechIndicators(void) { Deinit(); } //+------------------------------------------------------------------+ //| GetMomentum | //+------------------------------------------------------------------+ CMomentum* CTechIndicators::GetMomentum(void) { return m_momentum; } //+------------------------------------------------------------------+ //| CTechIndicators initialization | //+------------------------------------------------------------------+ bool CTechIndicators::Init(void) { // Initialization logic here... return true; } //+------------------------------------------------------------------+ //| CTechIndicators deinitialization | //+------------------------------------------------------------------+ void CTechIndicators::Deinit(void) { delete(m_momentum); Print("CTechIndicators deinitialization performed!"); } //+------------------------------------------------------------------+1.3.2. CMomentum, an Object-Oriented Wrapper for iMomentum
//+------------------------------------------------------------------+ //| CMomentum.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| CMomentum Class | //+------------------------------------------------------------------+ class CMomentum { protected: int m_handler; double m_buffer[]; public: //--- Constructor and destructor methods CMomentum(void); ~CMomentum(void); //--- Getter methods int GetHandler(void); void GetBuffer(double &buffer[], int ammount); //--- Setter methods bool SetHandler(string symbol,ENUM_TIMEFRAMES period,int mom_period,ENUM_APPLIED_PRICE mom_applied_price); bool UpdateBuffer(int ammount); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CMomentum::CMomentum(void) { ArraySetAsSeries(m_buffer, true); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ 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("Error creating Momentum indicator"); return false; } return true; } //+------------------------------------------------------------------+ //| UpdateBuffer | //+------------------------------------------------------------------+ bool CMomentum::UpdateBuffer(int ammount) { if(CopyBuffer(m_handler, 0, 0, ammount, m_buffer) < 0) { Alert("Error copying Momentum buffers, error: " , GetLastError()); return false; } return true; } //+------------------------------------------------------------------+
1.4. An Object-Oriented Container for the News
The news in abstract is a fundamental piece with which our EA has to deal with. We can think in this key piece as if it was a newspaper in order to conclude that it is a good idea encapsulating it in an object-oriented container of news. Put simply, this OO container, named CNewsContainer, is the newspaper. And of course if we can imagine a newspaper with news we also have to model the concept of the news which in our domain of things is named CNew. This is our custom object-oriented type representing real world's news.
1.4.1. CNewsContainer, the News' Container
//+------------------------------------------------------------------+ //| CNewsContainer.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <Files\FileTxt.mqh> #include <Arrays\ArrayObj.mqh> #include <..\Experts\NewsWatcher\CNew.mqh> //+------------------------------------------------------------------+ //| CNewsContainer Class | //+------------------------------------------------------------------+ class CNewsContainer { protected: string m_csv; // The name of the csv file CFileTxt m_fileTxt; // MQL5 file functionality int m_currentIndex; // The index of the next news to be processed in the container int m_total; // The total number of news to be processed CArrayObj *m_news; // News list in the computer's memory, loaded from the csv file public: //--- Constructor and destructor methods CNewsContainer(string csv); ~CNewsContainer(void); //--- Getter methods int GetCurrentIndex(void); int GetTotal(void); CNew *GetCurrentNew(); CArrayObj *GetNews(void); //--- Setter methods void SetCurrentIndex(int index); void SetTotal(int total); void SetNews(void); //--- CNewsContainer methods bool Init(); void Deinit(void); }; //+------------------------------------------------------------------+ //| Constuctor | //+------------------------------------------------------------------+ CNewsContainer::CNewsContainer(string csv) { m_csv=csv; m_news=new CArrayObj; SetNews(); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ 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) { //--- let's first init some vars! SetCurrentIndex(0); string sep= ";"; ushort u_sep; string substrings[]; u_sep=StringGetCharacter(sep,0); //--- then open and process the CSV file 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); //--- and finally refine and count the news m_news.Delete(0); // --- here we delete the CSV's header! SetTotal(m_news.Total()); } else { Print("Failed to open the file ",m_csv); Print("Error code ",GetLastError()); } } //+------------------------------------------------------------------+ //| CNewsContainer initialization | //+------------------------------------------------------------------+ bool CNewsContainer::Init(void) { // Initialization logic here... return true; } //+------------------------------------------------------------------+ //| CNewsContainer deinitialization | //+------------------------------------------------------------------+ void CNewsContainer::Deinit(void) { m_news.DeleteRange(0, m_total-1); delete(m_news); Print("CNewsContainer deinitialization performed!"); } //+------------------------------------------------------------------+
SetNews is the most important method of CNewsContainer. This method reads the CSV file and loads it into the computer's RAM in the form of objects of type CNew. By the way, I still have not said, CSV files must be stored in data_folder\MQL5\FILES\. Please take a look at File Functions for a deeper understanding of the functions used in SetNews.
1.4.2. CNew, the News Themselves
//+------------------------------------------------------------------+ //| CNew.mqh | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <Object.mqh> //+------------------------------------------------------------------+ //| CNew Class | //+------------------------------------------------------------------+ class CNew : public CObject { protected: string m_country; // The country's name datetime m_time_release; // The date and time of the news string m_name; // The name of the news public: //--- Constructor and destructor methods CNew(string country,datetime time_release,string name); ~CNew(void); //--- Getter methods string GetCountry(void); datetime GetTimeRelease(void); string GetName(void); //--- Setter methods void SetCountry(string country); void SetTimeRelease(datetime time_release); void SetName(string name); //--- CNew specific methods bool Init(); void Deinit(void); }; //+------------------------------------------------------------------+ //| Constuctor | //+------------------------------------------------------------------+ CNew::CNew(string country,datetime time_release,string name) { m_country=country; m_time_release=time_release; m_name=name; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ 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 initialization | //+------------------------------------------------------------------+ bool CNew::Init(void) { //--- initialization logic here... return true; } //+------------------------------------------------------------------+ //| CNew deinitialization | //+------------------------------------------------------------------+ void CNew::Deinit(void) { //--- deinitialization logic here... Print("CNew deinitialization performed!"); } //+------------------------------------------------------------------+
2. Backtesting ExpertNewsWatcher.mq5
2.1. Attachments
ExpertNewsWatcher is composed of the following files:
- Enums.mqh
- CBrain.mqh
- CEvolution.mqh
- CMomentum.mqh
- CNew.mqh
- CNewsContainer.mqh
- CNewsWatcher.mqh
- CTechIndicators.mqh
- ExpertNewsWatcher.mq5
- news_watcher.txt
2.2. Installation instructions
First of all, you have to create MQL5\Include\Mine folder to store your custom stuff, then please copy Enums.mqh file there. Right after that, you must create MQL5\Experts\NewsWatcher folder and copy the files below:
- CBrain.mqh
- CEvolution.mqh
- CMomentum.mqh
- CNew.mqh
- CNewsContainer.mqh
- CNewsWatcher.mqh
- CTechIndicators.mqh
- ExpertNewsWatcher.mq5
Very important note! Finally, please take news_watcher.txt, rename it to news_watcher.csv and put it in data_folder\MQL5\FILES\. At the time of the publication of this document, MQL5 form submission does not allow sending .csv files, but it does allow sending .txt files.
Do not forget to compile. From this point, you can backtest ExpertNewsWatcher as you would any other Expert Advisor.
2.3. Backtest results
ExpertNewsWatcher has been run with these initial input parameters.
- Period = 1 Minute
- StopLoss = 400
- TakeProfit = 600
- LotSize = 0.01
- CsvFile = news_watcher.csv
I initially used the following dummy data containing a set of fictitious news spaced in time to see how the robot behaved in a controlled environment. This is because those periods satisfy the established preconditions, that is, at those times the momentum is large enough to trigger the buy or sell actions. You can take this sheet of entries to test whatever you consider.
Some dummy data to store in 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
Figure 3. Results obtained with dummy data
The above graph containing fictitious news will help you to understand how this robot might behave in a real environment. Please take the following real data taken from DailyFX, place it in news_watcher.csv and run ExpertNewsWatcher again.
Some real data to store in 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
Figure 4. Results obtained with real data
This simple news processor can only respond to a single piece of news that takes place in a certain time. It is for this reason that a specific time, for instance, 2013.07.15 12:30:00, may contain several news. If several important news are happening at a given time, please write a single entry in the CSV file.
With that said, observe that the EA only puts three operations in the market when working with real data. This is because in real life some news will overlap, unlike the previous set of fictitious news spaced in time. Our robot is scheduled to first close the first operation that came from the series, ignoring an incoming piece of news when there is already an open position.
double momentumBuffer[]; GetTechIndicators().GetMomentum().GetBuffer(momentumBuffer, 2); //--- Number of seconds before the news releases. GMT +- timeWindow is the real time from which the robot starts //--- listening to the market. For instance, if there is a news release programmed at 13:00 GMT you can set TimeWindow //--- to 900 seconds so that the EA starts listening to the market fifteen minutes before that news release. int timeWindow=600; CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew(); int indexCurrentNew = GetBrain().GetNewsContainer().GetCurrentIndex(); if(TimeGMT() >= currentNew.GetTimeRelease() + timeWindow) { GetBrain().GetNewsContainer().SetCurrentIndex(indexCurrentNew+1); return; } //--- is there any open position? if(!m_positionInfo.Select(_Symbol)) { //--- if there is no open position, we try to open one 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); } } //--- if there is an open position, we let it work the mathematical expectation else { GetEvolution().SetStatus(DO_NOTHING); }
Conclusion
This has been the continuation of Another MQL5 OOP class article, which showed you how to build a simple OO EA from scratch and gave you some tips on object-oriented programming. Following the same line, this text has given you the necessary tools to help you build your own news traders. We have covered the implementation of object-oriented containers and object-oriented wrappers in order for us to comfortably work with our OO designs. We have also discussed MQL5 Standard Library and MQL5 functions to work with the file system.
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
Forum on trading, automated trading systems and testing trading strategies
Press review
newdigital, 2014.06.06 09:25
3 Steps to Trade Major News Events (based on dailyfx article)
Talking Points:
- News releases can be stressful on traders
- Develop a plan before the event arrives
Major news releases can be stressful on traders. That stress can show up for a variety of trading styles.Perhaps you are already in a good position with a good entry and you are afraid the news release may take a bite out of your good entry.
Perhaps you want to enter into a new position as prices are near a technically sound entry point, but you are uncertain if the technical picture will hold up through the volatile release. Therefore, you agonize over the decision of whether to enter now or after the news event.
Maybe, you like to be in the action and initiating new positions during the release. The fast paced volatility during the news release still gets makes your palms sweat as you place trades.
As you can see, news events stress traders in a variety of ways.
Today, we are going to cover three steps to trade news events.
Step 1 - Have a Strategy
It sounds simple, yet the emotion of the release can easily draw us off course. We see prices moving quickly in a straight line and are afraid to miss out or afraid to lose the gains we have been sitting on. Therefore, we make an emotional decision and act.
Having a strategy doesn’t have to be complicated. Remember, staying out of the market during news and doing nothing is a strategy.
A strategy for the trader with a floating profit entering the news event could be as simple as “I am going to close off half my position and move my stop loss to better than break even.”
For the trader wanting to initiate a new position that is technically based, they may decide to wait until at least 15 minutes after the release, then decide if the set-up is still valid.
The active news trader may realize they need a plan of buy and sell rules because they trade based on what ‘feels good.’
Step 2 - Use Conservative Leverage
If you are in the market when the news is released, make sure you are implementing conservative amounts of leverage. We don’t know where the prices may go and during releases, prices tend to move fast. Therefore, de-emphasize the influence of each trade on your account equity by using low amounts of leverage.
Our Traits of Successful Traders research found that traders who implement less than ten times effective leverage tend to be more profitable on average.
3 - Don’t Deviate from the Strategy
If you have taken the time to think about a strategy from step number one and if you have realized the importance of being conservatively levered, then you are 90% of the way there! However, this last 10% can arguably be the most difficult. Whatever your plan is, stick to it!
If I put together a plan to lose 20 pounds of body weight that includes eating healthier and exercising, but I continue to eat high fat and sugar foods with limited exercise, then I am only setting myself up for frustration.
You don’t have to be stressed or frustrated through fundamental news releases.
I have not found the Holy Grail yet but thanks to what I read I think I get closer to it every day .
That's nice to give us the necessary tools to help us build our own news traders. Thanks
Dear Jordi,
Your article is exelent,but while attached the EA I saw like that (in file attachment in below).
What is that happened? What wrong is?
Thank you for your innovation sharing.