Einen automatisierten News-Trader kreieren
Einleitung
Wie von Investopedia treffend beschrieben, handelt es sich bei einem News-Trader um „einen Trader oder Investor, dessen Handelsentscheidungen auf News & Nachrichten basieren“. Tatsächlich bewirken Dokumente wie beispielsweise Konjunkturberichte betreffend das BIP, Verbrauchervertrauensindexe und Arbeitslosenstatistiken eines Landes oft signifikante Veränderungen des Währungsmarktes. Haben Sie zum Beispiel jemals eine US-Non-Farm-Payroll-Veröffentlichung erlebt? Falls ja, dann wissen Sie wahrscheinlich bereits, dass diese Reporte die Zukunft einer Währung zu beeinflussen vermögen und dass sie als Katalysator für Trendwenden angesehen werden können.
Abbildung 1 Schwarzweiß-Zeitungen. Unter einer Creative Commons Lizenz auf Flickr veröffentlichtes Bild
1. Wir programmieren unseren EA
1.1 Die Idee des Handelssystems
Die Idee, die hinter diesem System steckt, haben wir oben diskutiert. Das hört sich natürlich cool an, aber wie können wir diese Tatsache mit der Welt des Programmierens in Verbindung bringen? Wir vertrauen primär auf zwei Aspekte von MQL5. Einerseits verwenden wir einen sogenannten Momentum-Indikator, um den Einfluss einer News auf ein Währungspaar zu messen. Andererseits werden wir mit MGL5-Dateifunktionen arbeiten, um unseren favorisierten News-Kalender in das Dateisystem zu integrieren und in ihm zu speichern. Wir streben dabei das Dateiformat CSV an. Wir werden diesen Roboter dahingehend programmieren, als dass wir das objektorientierte Paradigma sowie den bereits diskutierten, konzeptuellen Ansatz (Eine anderere MQL5-OOP-Klasse verwenden. Unser objektorientiertes Design wird die CSV-Datei in den Speicher des Computers laden, damit der EA aufgrund dieser Informationen verschiedene Entscheidungen treffen kann.
1.2 Zum OOP-Skelett des Roboters
Wir werden unsere EAs - von einem konzeptuellen Standpunkt her betrachtet - von nun an als lebende Organismen ansehen. Wir sind also jetzt objektorientierte Programmierer, erinnern Sie sich? Folglich werden wir insbesondere die Begriff Gehirn, Evolution, Indikatorset und News-Set verwenden. Ich werde all diese Dinge weiter unten ausführlicher erklären.
//+---------------------------------------------------------------------+ //| ExpertNewsWatcher.mq5 | //| Copyright © 2013, laplacianlab, Jordi Bassagañas | //+---------------------------------------------------------------------+ #property copyright "Copyright © 2013, laplacianlab. Jordi Bassagañas" #property link "https://www.mql5.com/de/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); } //+------------------------------------------------------------------+
Bei CNewsWatcher handelt es sich um die Hauptklasse des EAs. Sehen wir uns den Code an.
//+---------------------------------------------------------------------+ //| 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."); } //+------------------------------------------------------------------+
Seien Sie nicht beunruhigt, wenn Ihnen dieser zu Anfang ein wenig komplex erscheint, das ist ganz normal. Zunächst einmal müssen Sie alle Komponenten dieses EAs kennenlernen, um zu verstehen, wie er funktioniert. Ich empfehle Ihnen daher, vorliegenden Artikel zuerst zu überfliegen, um ihn im Anschluss daran noch einmal 2-3 Mal intensiver zu lesen. Wie auch immer Sie sich entscheiden sollten - ich würde Ihnen an dieser Stelle sehr gerne einiges zu den wichtigsten Teilen von CNewsWatcher kundtun.
Das wichtigste Element ist selbstverständlich die Methode OnTick, anhand der Sie erkennen, dass CNewsWatcher mit einem objektorientierten News-Container interagiert. Dieses Objekt, das als eine reale Zeitung betrachtet werden kann, enthält News, mit denen der Nutzer des EAs traden will.
Wir rufen den News-Container folgendermaßen ab:
GetBrain().GetNewsContainer();
Und wir rufen die aktuelle News ab, damit diese wie folgt verarbeitet wird:
CNew *currentNew = GetBrain().GetNewsContainer().GetCurrentNew();
Dies ist die Aufgabe von CBrain. Bitte erinnern Sie sich daran, dass CBrain ein wesentlicher Punkt unseres objektorientierten Designs darstellt, das all jene Komponenten besitzt, damit der EA richtig funktioniert. Es ist also eine Art Nur-Lese-Speicher (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 liest - einfach ausgedrückt - Schritt für Schritt eine jede News aus, die im Container (die Zeitung) gespeichert ist. Sollte es zu dieser Zeit eine besonders starke Beschleunigung des Preisanstiegs geben, dann wird eine Market Order platziert.
Was dabei das Kaufen/Verkaufen von Lots betrifft, so ist der Roboter reaktiv programmiert. Falls es also beispielsweise zu einer starken Aufwärtsbewegung käme, so ginge der EA davon aus, dass der Preis zurückgeht, wodurch er verkaufen würden. Wenn es analog hierzu eine entsprechende Abwärtsbewegung gäbe, würde der Roboter mit einer Long Position antworten, da er in Kürze mit einer Preisverbesserung rechnen würde. Dies kann natürlich noch weiter verbessert werden. Allerdings weist der Artikel nicht genügend Platz auf, um einen hocheffektiven, automatisierten News-Trader zu entwickeln. Wie bereits gesagt, möchte ich Ihnen lediglich einige Basiskenntnisse vermitteln, sodass Sie selbst befähigt werden, Ihre eigenen Entwicklungen/Strategien zu verbessern.
Abbildung 2 Roboter & Taff. Unter einer Creative Commons Lizenz auf Flickr veröffentlichtes Bild
1.3 Ein objektorientierter Container für technische Indikatoren
Noch einmal: Da wir uns entschieden haben, unsere Apps aus einem konzeptuellen Blickwinkel heraus zu betrachten, haben wir das Ziel, unsere eigenen objektorientierten Wrappers für technische Indikatoren zu programmieren, die mit unserem neuen Paradigma im Einklang stehen. Daher passt dieses Puzzlestück so gut zu allen anderen. Ich möchte im Folgenden dazu kommen, eine Art objektorientiertes Framework zu konstruieren, mit dem wir auf äußerst komfortable Weise mit MQL5-Elementen arbeiten können.
An dieser Stelle sei kurz auf die MQL5-Standard-Libary verwiesen. Diese Bibliothek wurde speziell dafür designt, Schreibprogramme von Endbenutzern zu unterstützen (Indikatoren, Skripte, Experts), um diesen den Zugriff auf verschiedene vorteilhafte internen Funktionen von MQL5 zu ermöglichen. Tatsächlich werden wir in der vorliegenden Übung auf einige Funktionen der Standard-Library zugreifen, da es sich aus einem OO-Blickwinkel gerade zu anbietet, dies zu tun. Ein hervorragendes Beispiel hierfür wäre der News-Container, den ich später noch ein wenig näher beschreiben will: So werden wir die MQL5-Klasse CArrayObj verwenden, um unseren objektorientierten, komplexen News-Typ im Arbeitsspeicher des Rechners abzulegen.
Studieren Sie bitte das Dokument Standard-Library, um ein wenig mehr über dieses Thema zu erfahren - wie beispielsweise den Umstand, dass die Standard-Library bereits über einige Klassen zum Arbeiten mit Indikatoren verfügt. Dieser Artikel widmet sich der Notwendigkeit, sich mit objektorientierten Materialien auseinanderzusetzen, indem wir uns einige Lehrbeispiele ansehen.
//+------------------------------------------------------------------+ //| 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 - ein objektorientierter Wrapper für 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 Ein objektorientierter Container für die Nachrichten
Abstrakte News stellen eines der fundamentalen Elemente dar, mit denen sich ein EA beschäftigen muss. Stellen wir uns eine Zeitung vor, die wir in einen objektorientierten News-Container einkapseln und verschließen. Einfach ausgedrückt: Der objektorientierte CNewsContainer ist nichts anderes als eine handelsübliche Gazette. CNew stellt dabei den Namen des Modells des News-Konzepts dar. Hierbei handelt es sich um unseren OO-Typ, der eine reale News repräsentiert.
1.4.1 CNewsContainer, der Container für die News
//+------------------------------------------------------------------+ //| 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 stellt das für uns möglicherweise wichtigste Methode von NewsContainer dar. Diese Methode liest die Informationen der CSV-Datei aus und lädt sie in Form eines CNew-Objekts in den Arbeitsspeicher des Rechners. Ich habe im Übrigen noch nicht erwähnt, dass sämtliche CSV-Dateien in dem Ordner data_folder\MQL5\FILES\ gespeichert werden müssen. Werfen Sie bitte einen Blick auf Dateifunktionen, um mehr Informationen betreffend die Funktionen zu erhalten, die in SetNews Verwendung finden.
1.4.2 CNew, die News höchstselbst
//+------------------------------------------------------------------+ //| 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. ExpertNewsWatcher.mq5 im Backtest
2.1 Anhänge
ExpertNewsWatcher besteht aus den folgenden Dateien:
- Enums.mqh
- CBrain.mqh
- CEvolution.mqh
- CMomentum.mqh
- CNew.mqh
- CNewsContainer.mqh
- CNewsWatcher.mqh
- CTechIndicators.mqh
- ExpertNewsWatcher.mq5
- news_watcher.txt
2.2 Installationsanweisungen
Zunächst einmal müssen Sie einen adäquaten Ordner erstellen, der Ihre zukünftigen, geänderten Dateien aufbewahren soll: MQL5\Include\Mine. Kopieren Sie danach bitte die Datei Enums.mqh hinein. Direkt danach müssen Sie den Ordner MQL5\Experts\NewsWatcher anlegen und die nachfolgenden Dateien nach dort kopieren:
- CBrain.mqh
- CEvolution.mqh
- CMomentum.mqh
- CNew.mqh
- CNewsContainer.mqh
- CNewsWatcher.mqh
- CTechIndicators.mqh
- ExpertNewsWatcher.mq5
Ein wichtiger Hinweis! Schließlich sollten Sie sich noch der Datei news_watcher.txt zuwenden: Benennen Sie diese in news_watcher.csv um und stecken Sie sie in den Ordner data_folder\MQL5\FILES\. Zum Zeitpunkt der Veröffentlichung dieses Artikels wird die Versendung von .csv-Dateien seitens MQL5 nicht unterstützt. Allerdings erlaubt MQL5 das Senden von .txt.Dateien.
Vergessen Sie nicht, das Ganze zu kompilieren. Nun können Sie ExpertNewsWatcher einem Backtest unterziehen, so wie Sie es mit jedem anderen EA tun würden.
2.3 Backtest-Resultate
ExpertNewsWatcher wurde anhand folgender Eingabeparameter getestet:
- Periode = 1 Minute
- StopLoss = 400
- TakeProfit = 600
- LotGröße = 0.01
- CsvDatei = news_watcher.csv
Ursprünglich habe ich folgende Dummy-Daten (die fiktive News enthalten) verwendet, um zu sehen, wie sich unser EA innerhalb einer kontrollierten Umgebung verhält. Grund: Diese Perioden entsprechen genau den Vorbedingungen, das heißt, während dieser Zeiträume ist das Momentum groß genug, um Kaufen- oder VerkaufenAktionen auszulösen. Sie können mithilfe dieses Stylesheets testen, was auch immer Sie wollen.
Einige Dummy-Daten, um sie in news_watcher.csv zu speichern:
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
Abbildung 3 Mit Dummy-Daten erhaltene Ergebnisse
Der obige, fiktive News enthaltende Graph hilft Ihnen dabei, zu verstehen, wie sich der Roboter in einer realen Umgebung verhalten könnte. Bedienen Sie sich nun bitte der folgenden, realen Daten von DailyFX, füttern Sie damit news_watcher.csv und führen Sie noch einmal ExpertNewsWatcher aus.
Einige reale Daten, um sie in news_watcher.csv zu speichern.
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
Abbildung 4 Mit reellen Daten erhaltene Ergebnisse
Dieser einfache News-Prozessor kann nur einzelne News verarbeiten, die sich zu einer bestimmten Zeit ereignen. Nun gibt es den Fall, dass eine spezifische Zeit (Bsp: 15.07.2013 12:30:00) mehrere relevante News enthält. Falls zu einer bestimmten Zeit mehrere wichtige News gleichzeitig auftreten sollten, so würde ich Sie bitten, die CSV-Datei mit einem einzigen Eintrag zu versehen.
Nachdem dies gesagt wurde, werden Sie feststellen, dass der EA nur drei Marktoperationen gleichzeitig durchführt, falls er mit realen Daten jongliert. Die Ursache hierfür liegt darin begründet, dass sich einige reale News im Gegensatz zu fiktiven überlappen könnten. Unser Roboter ist so eingestellt, dass er zuerst die erste Order einer Serie schließen und eingehende News ignorieren wird, falls bereits eine offene Order existiert.
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); }
Fazit
Vorliegender Artikel stellt eine Fortsetzung des Artikels Eine andere MQL5-OOP-Klasse dar, der Ihnen bereits Tipps zum objektorientierten Programmieren vermittelt und der Ihnen gezeigt hat, wie Sie aus dem Nichts einen objektorientierten EA basteln. Gleichzeitig hat Ihnen dieser Text all die Mittel an die Hand gegeben, die Sie zur Entwicklung Ihres eigenen News-Traders benötigen. Wir haben außerdem über die Implementation objektorientierter Container / Wrappers gesprochen, um den komfortablen Umgang mit OO-Designs zu erlernen. Schließlich sind wir noch auf die MQL5-Standard-Library zu sprechen gekommen und haben uns angesehen, auf welche Art und Weise MQL5-Funktionen mit dem Dateisystem interagieren.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/719
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.