![Eine andere MQL5-OOP-Klasse](https://c.mql5.com/i/articles/overlay.png)
Eine andere MQL5-OOP-Klasse
Einleitung
Einen kompletten objektorientierten EA zusammenzubauen, der dann auch noch funktioniert, ist meiner Meinung nach gar keine so einfache Angelegenheit: Der Roboter benötigt ein logisches Denkvermögen, er muss analysieren wie auch synthetisieren können und darüber hinaus über eine gewisse Vorstellungskraft verfügen. Verglichen wir das automatische Handelssystem, das wir haben, mit einer Partie Schach, dann wäre die Handelsidee nichts anderes als die Schachstrategie. Und die Ausführung dieser Schachstrategie mihilfe von Taktiken entspräche dann dem Programmieren des Roboters, indem technische Indikatoren, Charts sowie ökonomische Klassen und konzeptionelle Axiome verwendet werden.
Abbildung 1 Details zu Raffaello Sanzios The School of Athens. In diesem Bild sehen wir die beiden Philosophen Plato und Aristoteles, tief in ein Gespräch vertieft.
Plato verkörpert dabei die konzeptionelle und Aristoteles die empirische Welt.
Und ja, ich bin mir der Schwierigkeiten dieser Übung vollends bewusst. Ein objektorientierter EA ist nicht allzu schwer zu programmieren, gleichwohl stimmt es natürlich, dass es Personen entsprechend schwer fällt, die im Bereich der App-Entwicklungen nur wenig Erfahrungen aufweisen Ich werde daher versuchen, möglichst mit spezifischen Beispielen zu agieren, in der Hoffnung, dass es dadurch ein bisschen nachvollziehbarer wird. Fühlen Sie sich bitte nicht entmutigt, falls Sie dennoch Unsicherheiten bei der Verwendung von OOP-Konzepten verspüren. Nachdem Sie Ihren ersten EA konzipiert haben, sieht die ganze Sache schon wieder anders aus. Denken Sie dabei bitte immer daran, dass diese Konzeption nicht bei 0 beginnen muss.
Der gesamte Prozess des Konzipierens und Implementierens eines Handelssystems kann durch die Zusammenarbeit von mehreren Menschen beträchtlich simplifiziert werden - eine gewisse Kommunikation unter diesen Menschen vorausgesetzt. Ich meine damit, dass die Person, die eine Investmentstrategie konzipiert, nicht zwangsläufig dazu befähigt sein muss, den entsprechenden Programmcode zu schreiben - dies ist die Aufgabe eines Programmierers. Und vielleicht versteht auch der der MQL5-Entwickler anfangs einige der wichtigsten Aspekte der Handelsstrategie seines Kunden nicht.
Dies ist ein klassisches Problem bei der Softwareprogrammierung, das letztendlich zur Entstehung vieler verschiedener Softwareentwicklungsmethodiken geführt hat: Scrum, Test-Driven-Development (TDD), Extreme-Programming (XP). Man sollte sich daher der Fallgruben einer Sprache stets bewusst sein Im Übrigen findet sich auf Wikipedia folgender Hinweis: „Softwareentwicklungs oder Systementwicklungsmethodiken stellen innerhalb der Softwareentwicklung ein Framework dar, das dazu benutzt wird, den Prozess der Entwicklung eines IT-Systems zu strukturieren, zu planen und zu kontrollieren“ (übersetzt aus dem Englischen).
Wir werden im Folgenden davon ausgehen, dass wir zu beidem fähig sind: erfolgreiche HandelsIdeen zu konzipieren sowie zu implementieren. Wir können ferner davon ausgehen, dass wir uns am Ende eines iterativen Entwicklungsprozesses befinden, bei dem das Handelssystem, das durch unseren Kunden erdacht, bereits ausreichend definiert und seitens der meisten Leute verstanden worden ist. Je nachdem, was Ihnen beliebt. Übrigens werde ich von nun an innerhalb dieses Textes auf einige lehrreiche Artikel verweisen, die sich unter MQL5-Artikel zum Programmieren finden lassen. Auf diese Weise können Sie schnell einzelne Ideen detaillierter einsehen und etwaige Übungen durchführen. Sind Sie bereit?
1 Ihre ersten Schritte hin zur Aneignung des Neuen Paradigmas
1,1 Warum möchte ich meine Forex-EAs überhaupt objektorientiert programmieren?
Vielleicht fragen Sie sich an dieser Stelle, warum Sie einen OOP-basierten EA überhaupt in Erwägung ziehen sollten. An dieser Stelle sei Ihnen zunächst versichert, dass Sie nicht dazu gezwungen sind. Jedoch empfehle ich Ihnen, sich mit OOP-Prinzipien vertraut zu machen, und sei es nur, um Ihr Wissen in Bezug auf das Programmieren automatischer Handelssysteme zu erweitern.
Der klassische Weg des Programmierens - das sogenannte prozedurale Programmieren - besitzt dabei folgende Nachteile:
- Es erschwert die Modellierung von Problemen. Das alte Paradigma wartete zur Lösung des Hauptproblems mit folgendem Ansatz auf: Man reduziert es in mehrere kleinere, simplere Teilprobleme, die dann mittels funktionalen Modulen, also Funktionen und Prozeduren, gelöst werden sollen.
- Es erschwert die Wiederbenutzung von bereits geschriebenem Code. Darunter leiden nicht nur die Kosten, sondern ebenso die Zuverlässigkeit, die Flexibilität als auch die Instandhaltung.
Die Wiederbenutzung von verschiedenen Zeilen Code ist mit objektorientierter Programierung wesentlich einfacher. Das ist nun sehr wichtig! Viele Experten sind der Meinung, dass die Wiederverwendung älteren Codes DIE Lösung zu den meisten Softwareentwicklungsproblemen darstellen könnte.
An dieser Stelle sei kurz auf Abstrakt Datentypen (ADT) verwiesen. OOP erlaubt und begünstigt die Kreierung von ADTs. Unter ADT wollen wir an dieser Stelle eine Abstraktion des traditionellen Datentypkonzepts verstehen, das in allen Programmiersprachen präsent ist. Dessen Hauptzweck ist eine komfortable Definition des Datenbereichs einer Applikation. Bei Include\Arrays\Array.mqh, Include\Arrays\List.mqh und Include\Arrays\Tree.mqh handelt es sich um Beispiele abstrakter Datentypen, auf die Sie während der Interaktion mit MQL5 stoßen werden.
Kurz gesagt: Das Paradigma des objektorientierten Programmierens zielt darauf ab, dass Sie Ihre Applikationen so designen können, dass das Wiederaufgreifen alten Codes, Zuverlässigkeit, Flexibilität als auch die Instandhaltung größtmöglich erleichtert werden.
1,2 Sind Sie ein Denker und ein Freund von Konzepten? UML ist hier, um Sie zu retten
Haben Sie schon einmal etwas von UML gehört? UML steht an dieser Stelle für Unified Modeling Language, also vereinheitlichte Modellierungssprache. Es ist eine grafische Programmiersprache, um objektorientierte Systeme zu designen. Wir Menschen denken normalerweise bei unseren Systemen zuerst an eine Analysephase und im Anschluss daran an das Schreiben eines Codes via Programmiersprache. Von oben nach unten zu gehen, erscheint Programmierern weniger absurd. Gleichwohl haben mich meine Erfahrungen als Analyst gelehrt, dass dies manchmal nicht möglich ist, und zwar aus folgenden Gründen: Die App muss in einer sehr kurzen Zeit geschrieben werden. Es gibt kein Teammitglied, das seine UML-Kenntnisse entsprechend schnell anbringen kann. Oder einige Mitglieder des Teams kennen einfach verschiedene UML-Abschnitte schlicht und ergreifend nicht.
UML ist meiner Meinung nach ein sehr gutes Analysetool, falls Sie die notwendigen Kenntnisse mitbringen und falls die Umstände des Projekts es zulassen. Sollten Sie sich mit UML etwas genauer vertraut machen wollen, empfehle ich Ihnen als Einstieg folgende Lektüre: Wie man einen EA mithilfe von UML-Tools entwickelt. Dieser Artikel erschlägt Sie möglicherweise anfangs ein wenig, stellt aber dennoch einen sehr guten Einstieg dar, wenn Sie daran interessiert sind, wie professionelle Softwareentwickler mit Analysen arbeiten. Um UML vollständig zu begreifen, würden Sie wahrscheinlich einen mehrwöchigen Kurs belegen müssen! Aber ich denke, dass es für den Moment genügen sollte, wenn Sie einfach nur wissen, was UML ist. Meinen Erfahrungen nach gibt es einige Bedingungen, die die Anwendung dieses Analysetools bei manchen Softwareprojekten (in der realen Welt) einfach unterbinden.
Abbildung 2 UML-Logo
1.3 Hallo Welt! Ihre erste OO-Klasse
Wenn es sich bei Ihnen um einen kompletten Neuling in Bezug auf objektorientiertes Programmieren handeln sollte, sollten sie zuerst einige der unter MQL5-Verweise befindlichen OO-Dokumente lesen und daraufhin den Artikel Einen MQL5-EA mittels objektorientierter Programmierung schreiben studieren. In jedem Fall sollten Sie die dabei erworbenen Erkenntnisse durch die Lektüre anderer, weiterführender Literatur ergänzen. Ich gehe von nun an davon aus, dass Sie bereits ein gewisses Vorwissen im Bereich OOP besitzen. Folglich gehe ich davon aus, dass Ihnen das Verständnis folgenden Beispiels keine Probleme macht:
//+------------------------------------------------------------------+ //| CPerson Class | //+------------------------------------------------------------------+ class CPerson { protected: string m_first_name; string m_surname; datetime m_birth; public: //--- Constructor and destructor methods CPerson(void); ~CPerson(void); //--- Getter methods string GetFirstName(void); string GetSurname(void); datetime GetBirth(void); //--- Setter methods void SetFirstName(string first_name); void SetSurname(string surname); void SetBirth(datetime birth); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPerson::CPerson(void) { Alert("Hello world! I am run when an object of type CPerson is created!"); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CPerson::~CPerson(void) { Alert("Goodbye world! I am run when the object is destroyed!"); } //+------------------------------------------------------------------+ //| GetFirstName | //+------------------------------------------------------------------+ string CPerson::GetFirstName(void) { return m_first_name; } //+------------------------------------------------------------------+ //| GetSurname | //+------------------------------------------------------------------+ string CPerson::GetSurname(void) { return m_surname; } //+------------------------------------------------------------------+ //| GetBirth | //+------------------------------------------------------------------+ datetime CPerson::GetBirth(void) { return m_birth; } //+------------------------------------------------------------------+ //| SetFirstName | //+------------------------------------------------------------------+ void CPerson::SetFirstName(string first_name) { m_first_name=first_name; } //+------------------------------------------------------------------+ //| SetSurname | //+------------------------------------------------------------------+ void CPerson::SetSurname(string surname) { m_surname=surname; } //+------------------------------------------------------------------+ //| SetBirth | //+------------------------------------------------------------------+ void CPerson::SetBirth(datetime birth) { m_birth=birth; } //+------------------------------------------------------------------+
Das Folgende richtet sich zweifellos an Entwickler, die durch und durch in die Qualität Ihrer Codes vernarrt sind. Im Gegensatz zu einigen Beispielen, die Sie in unter MQL5-Artikel zum Programmieren finden sowie innerhalb unserer Code-Datenbank finden, benutzt die obige Klasse die selben Programmierkonditionen, die von MetaQuotes Software Corp. bei der Programmierung ihres MQL5-Frameworks verwendet wurden. Ich ermutige Sie an dieser Stelle darum bitten, Ihren Code so zu schreiben, wie es MetaQutoes tun würde. Im Übrigen wird dieses Thema ferner in folgendem Thread behandelt: Zu den Konventionen von OOP in MQL5-Programmen.
Um es kurz zu machen, hier einige wichtige Konventionen beim Schreiben von Person.mqh:
- Der Klassenname - CPerson - starte mit einem großen C.
- Methodennamen basieren auf dem Binnenmajuskel-Prinzip (CamelCased) und starten mit einem Großbuchstaben: GetFirstName, SetSurname, usw.
- Geschützten Eigenschaften geht die Vorsilbe m_ voraus. Einige Beispiele: m_first_name, m_surname und m_birth.
- Das reservierte Wort this dient nicht dazu, die Mitglieder einer Klasse in Richtung der Klasse selbst zu verweisen.
Sehen Sie sich bitte einige MQL5-Framework-Dateien wie beispielsweise Include\Arrays\Array.mqh, Include\Arrays\List.mqh und Include\Trade\Trade.mqh an, um festzustellen, in welcher Art und Weise der ursprüngliche MQL5-Code geschrieben ist.
2 Lassen Sie uns Ihren ersten objektorientierten EA programmieren
2,1 Die Idee des Handelssystems
Unsere Handelsidee ist relativ einfach: „Kurze Trends eines volatilen Marktes sind nahezu zufällig“. Das ist alles! Dieses Ergebnis wurde bereits von einigen Experten unter einigen Umständen beobachtet. Falls diese Hypothese korrekt ist, müsste unser Forex-Roboter auf jeden Fall funktionieren. Wenn man sich einen zufälligen Punkt auf dem Chart heraussucht, kann die nächste Bewegung entweder in Richtung Decke oder Boden gehen, wir wissen es einfach nicht. Das Problem ist, dass, wenn der Unterschied zwischen den SL- und TP-Levels ausreichend klein ist, die Differenz - in absoluten Zahlen - nicht signifikant ist, wodurch wir die mathematischen Erwartungen erreichen. Dieses System wird also auf den mathematischen Erwartungen basieren. Sobald Sie den Code des EAs dieses Artikels einem Backtest unterziehen, werden Sie feststellen, dass er nur einer sehr simplen Money-Management-Methode bedarf.
2,2 Zum OOP-Skelett des Roboters
In diesem Abschnitt werden wir die obige Strategie durch jene abstrakten Überlegungen entwickeln, die für objektorientiertes Programmieren unabdingbar sind. Warum also betrachten wir unseren EA nicht einfach als eine Lebensform? Eine derartige Annahme brächte uns zu dem Schluss, dass unser Forex-Roboter im Großen und Ganzen aus drei Teilen besteht: einem Gehirn, einem Chart und aus etwas, das wir Evolution nennen wollen.
Das Gehirn ist derjenige Teil des Roboters, der über die notwendigen Operationsdaten verfügt, also so eine Art von Festwertspeicher/read-only-memory (ROM). Der Chart ist das Informationselement, das diejenige Grafik emuliert, entsprechend der der Roboter operiert. Drittens und letztens, was die sogenannte Evolution betrifft, so handelt es sich dabei um den Teil der Daten, der temporäre Informationen wie beispielsweise den Status des Roboters zu jedem beliebigen Zeitpunkt oder zum Beispiel die Historie an durchgeführten Operationen enthält. Es ist fast so, als ob wir einen Menschen samt all seiner Organe zusammenbauten - eine Art Frankenstein - da wir eine App für den Gesundheitsabschnitt entwickeln müssen. In diesem Kontext ist ein jedes Organ als ein einzigartiges, semantisches Konzept zu verstehen, dass mit einigen anderen Teilen ein großes Ganzes bildet.
Lassen Sie uns doch zuallererst einmal den Ordner MQL5\Include\Mine anlegen, um darin unsere modifizierten Dateien zu speichern. Dies ist lediglich eine Möglichkeit, Ihren Code strukturiert zu organisieren. Es ist gut, zu wissen, dass Sie dies mit all Ihren Entwicklungsdateien tun können, aber Sie sind natürlich nicht dazu gezwungen. Wir werden dann die Datei QL5\Include\Mine\Enums.mqh anlegen, um die von uns kreierten Enums zu speichern:
//+------------------------------------------------------------------+ //| Status enumeration | //+------------------------------------------------------------------+ enum ENUM_STATUS_EA { BUY, SELL, DO_NOTHING }; //+------------------------------------------------------------------+ //| Lifetime enumeration | //+------------------------------------------------------------------+ enum ENUM_LIFE_EA { HOUR, DAY, WEEK, MONTH, YEAR }; //+------------------------------------------------------------------+
Der nächste Punkt auf der Liste ist die Erschaffung unseres EA-Embryos, den wir ExpertSimpleRandom.mq5 nennen wollen! Legen Sie also bitte den Ordner MQL5\Experts\SimpleRandom inklusive der darin befindlichen Datei ExpertSimpleRandom.mq5 mit dem folgenden Code an:
//+------------------------------------------------------------------+ //| ExpertSimpleRandom.mq5 | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #property copyright "Copyright © 2013, laplacianlab" #property link "https://www.mql5.com/de/articles" #property version "1.00" #include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Indicators\Indicators.mqh> #include <Mine\Enums.mqh> #include <..\Experts\SimpleRandom\CSimpleRandom.mqh> input int StopLoss; input int TakeProfit; input double LotSize; input ENUM_LIFE_EA TimeLife; MqlTick tick; CSimpleRandom *SR=new CSimpleRandom(StopLoss,TakeProfit,LotSize,TimeLife); //+------------------------------------------------------------------+ //| Initialization function | //+------------------------------------------------------------------+ int OnInit(void) { SR.Init(); return(0); } //+------------------------------------------------------------------+ //| Deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { SR.Deinit(); delete(SR); } //+------------------------------------------------------------------+ //| OnTick event function | //+------------------------------------------------------------------+ void OnTick() { SymbolInfoTick(_Symbol,tick); SR.Go(tick.ask,tick.bid); } //+------------------------------------------------------------------+
Es handelt sich hierbei nur um eine Methode von vielen. Und all dies dient natürlich nur dazu, Ihnen die Interaktion von OOP und MQL5 zu verdeutlichen. Wie Sie sehen, trägt die Hauptklasse des EAs den Namen CSimpleRandom.mqh. Speichern Sie diese bitte unter MQL5\Experts\SimpleRandom\CSimpleRandom.mqh ab:
//+------------------------------------------------------------------+ //| ExpertSimpleRandom.mq5 | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> #include <Mine\Enums.mqh> #include <..\Experts\SimpleRandom\CBrain.mqh> #include <..\Experts\SimpleRandom\CEvolution.mqh> #include <..\Experts\SimpleRandom\CGraphic.mqh> //+------------------------------------------------------------------+ //| CSimpleRandom Class | //+------------------------------------------------------------------+ class CSimpleRandom { protected: CBrain *m_brain; CEvolution *m_evolution; CGraphic *m_graphic; CTrade *m_trade; CPositionInfo *m_positionInfo; public: //--- Constructor and destructor methods CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life); ~CSimpleRandom(void); //--- Getter methods CBrain *GetBrain(void); CEvolution *GetEvolution(void); CGraphic *GetGraphic(void); CTrade *GetTrade(void); CPositionInfo *GetPositionInfo(void); //--- Specific methods of CSimpleRandom bool Init(); void Deinit(void); bool Go(double ask,double bid); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSimpleRandom::CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life) { int lifeInSeconds; switch(time_life) { case HOUR: lifeInSeconds=3600; break; case DAY: lifeInSeconds=86400; break; case WEEK: lifeInSeconds=604800; break; case MONTH: lifeInSeconds=2592000; break; // One year default: lifeInSeconds=31536000; break; } m_brain=new CBrain(TimeLocal(),TimeLocal()+lifeInSeconds,lot_size,stop_loss,take_profit); m_evolution=new CEvolution(DO_NOTHING); m_graphic=new CGraphic(_Symbol); m_trade=new CTrade(); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSimpleRandom::~CSimpleRandom(void) { delete(m_brain); delete(m_evolution); delete(m_graphic); delete(m_trade); } //+------------------------------------------------------------------+ //| GetBrain | //+------------------------------------------------------------------+ CBrain *CSimpleRandom::GetBrain(void) { return m_brain; } //+------------------------------------------------------------------+ //| GetBrain | //+------------------------------------------------------------------+ CEvolution *CSimpleRandom::GetEvolution(void) { return m_evolution; } //+------------------------------------------------------------------+ //| GetGraphic | //+------------------------------------------------------------------+ CGraphic *CSimpleRandom::GetGraphic(void) { return m_graphic; } //+------------------------------------------------------------------+ //| GetTrade | //+------------------------------------------------------------------+ CTrade *CSimpleRandom::GetTrade(void) { return m_trade; } //+------------------------------------------------------------------+ //| GetPositionInfo | //+------------------------------------------------------------------+ CPositionInfo *CSimpleRandom::GetPositionInfo(void) { return m_positionInfo; } //+------------------------------------------------------------------+ //| CSimpleRandom initialization | //+------------------------------------------------------------------+ bool CSimpleRandom::Init(void) { // Initialization logic here... return true; } //+------------------------------------------------------------------+ //| CSimpleRandom deinitialization | //+------------------------------------------------------------------+ void CSimpleRandom::Deinit(void) { // Deinitialization logic here... delete(m_brain); delete(m_evolution); delete(m_graphic); delete(m_trade); } //+------------------------------------------------------------------+ //| CSimpleRandom Go | //+------------------------------------------------------------------+ bool CSimpleRandom::Go(double ask,double bid) { double tp; double sl; int coin=m_brain.GetRandomNumber(0,1); // Is there any open position? if(!m_positionInfo.Select(_Symbol)) { // If not, we open one if(coin==0) { GetEvolution().SetStatus(BUY); } else { GetEvolution().SetStatus(SELL); } } // If so, let it work the mathematical expectation. else GetEvolution().SetStatus(DO_NOTHING); 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; } // If there is some error we return false, for now we always return true return(true); } //+------------------------------------------------------------------+
2,3 CSimpleRandom an Complex-Type-Objekte binden
Haben Sie gesehen, auf welche Art und Weise die spezifizierten Objekttypen CBrain, CEvolution und CGraphic mit CSimpleRandom in Verbindung stehen?
Wir werden nun die entsprechenden geschützten Eigenschaften definieren:
protected:
CBrain *m_brain;
CEvolution *m_evolution;
CGraphic *m_graphic;
Und gleich danach heißt es, eine Instanz dieser Objekte im Konstruktor zu erstellen.
m_brain=new CBrain(TimeLocal(), TimeLocal() + lifeInSeconds, lot_size, stop_loss, take_profit); m_evolution=new CEvolution(DO_NOTHING); m_graphic=new CGraphic(_Symbol);
Das, was wir genau tun, ist eine dynamische Generierung von Complex-Type-Objekten, genauso wie wir es unter Objekt-Pointers vorfinden. Mithilfe dieser Methode können wir direkt via CSimpleRandom auf die Funktion von CBrain, CEvolution und CGraphic zugreifen. Wir könnten also beispielsweise in ExpertSimpleRandom.mq5 den folgenden Code ausführen:
//+------------------------------------------------------------------+ //| OnTick event function | //+------------------------------------------------------------------+ void OnTick() { // ... int randNumber=SR.GetBrain().GetRandomNumber(4, 8); // ... }
Um diesen Abschnitt zu beenden, werde ich Ihnen noch den Code für CBrain, CEvolution sowie CGraphic niederschreiben. Nehmen Sie bitte zur Kenntnis, dass es einige Stellen gibt, die keinen Code besitzen, da diese schlicht und ergreifend nicht benötigt werden, um SimpleRandom einem Backtest zu unterziehen. Die fehlenden Teile des Codes dieser Klassen zu programmieren, liegt nun an Ihnen. Lassen Sie Ihrer Fantasie also freien Lauf. Beispielsweise wird m_death nicht tatsächlich benutzt, obwohl die dahinterstehende Idee eigentlich die ist, dass man bereits zu Beginn den Termin kennt, an dem der Roboter seine Aktivität einstellen wird.
//+------------------------------------------------------------------+ //| ExpertSimpleRandom | //| Copyright © 2013, Jordi Bassagaсas | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| CBrain Class | //+------------------------------------------------------------------+ class CBrain { protected: ENUM_TIMEFRAMES m_period; // period must always be initialized to PERIOD_M1 to fit the system's idea datetime m_birth; // The datetime in which the robot is initialized for the first time datetime m_death; // The datetime in which the robot will die double m_size; // The size of the positions int m_stopLoss; // Stop loss int m_takeProfit; // Take profit public: //--- Constructor and destructor methods CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit); ~CBrain(void); //--- Getter methods datetime GetBirth(void); datetime GetDeath(void); double GetSize(void); int GetStopLoss(void); int GetTakeProfit(void); //--- Setter methods void SetBirth(datetime birth); void SetDeath(datetime death); void SetSize(double size); void SetStopLoss(int stopLoss); void SetTakeProfit(int takeProfit); //--- Brain specific logic int GetRandomNumber(int a,int b); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CBrain::CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit) { MathSrand(GetTickCount()); m_period=PERIOD_M1; m_birth=birth; m_death=death; m_size=size; m_stopLoss=stopLoss; m_takeProfit=takeProfit; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CBrain::~CBrain(void) { } //+------------------------------------------------------------------+ //| GetBirth | //+------------------------------------------------------------------+ datetime CBrain::GetBirth(void) { return m_birth; } //+------------------------------------------------------------------+ //| GetDeath | //+------------------------------------------------------------------+ datetime CBrain::GetDeath(void) { return m_death; } //+------------------------------------------------------------------+ //| GetSize | //+------------------------------------------------------------------+ double CBrain::GetSize(void) { return m_size; } //+------------------------------------------------------------------+ //| GetStopLoss | //+------------------------------------------------------------------+ int CBrain::GetStopLoss(void) { return m_stopLoss; } //+------------------------------------------------------------------+ //| GetTakeProfit | //+------------------------------------------------------------------+ int CBrain::GetTakeProfit(void) { return m_takeProfit; } //+------------------------------------------------------------------+ //| SetBirth | //+------------------------------------------------------------------+ void CBrain::SetBirth(datetime birth) { m_birth=birth; } //+------------------------------------------------------------------+ //| SetDeath | //+------------------------------------------------------------------+ void CBrain::SetDeath(datetime death) { m_death=death; } //+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+ //| GetRandomNumber | //+------------------------------------------------------------------+ int CBrain::GetRandomNumber(int a,int b) { return(a+(MathRand()%(b-a+1))); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| ExpertSimpleRandom | //| Copyright © 2013, Jordi Bassagaсas | //+------------------------------------------------------------------+ #include <Indicators\Indicators.mqh> #include <Mine\Enums.mqh> //+------------------------------------------------------------------+ //| CEvolution Class | //+------------------------------------------------------------------+ class CEvolution { protected: ENUM_STATUS_EA m_status; // The current EA's status CArrayObj* m_operations; // History of the operations performed by the EA public: //--- Constructor and destructor methods CEvolution(ENUM_STATUS_EA status); ~CEvolution(void); //--- Getter methods ENUM_STATUS_EA GetStatus(void); CArrayObj *GetOperations(void); //--- Setter methods void SetStatus(ENUM_STATUS_EA status); void SetOperation(CObject *operation); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CEvolution::CEvolution(ENUM_STATUS_EA status) { m_status=status; m_operations=new CArrayObj; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CEvolution::~CEvolution(void) { delete(m_operations); } //+------------------------------------------------------------------+ //| GetStatus | //+------------------------------------------------------------------+ ENUM_STATUS_EA CEvolution::GetStatus(void) { return m_status; } //+------------------------------------------------------------------+ //| GetOperations | //+------------------------------------------------------------------+ CArrayObj *CEvolution::GetOperations(void) { return m_operations; } //+------------------------------------------------------------------+ //| SetStatus | //+------------------------------------------------------------------+ void CEvolution::SetStatus(ENUM_STATUS_EA status) { m_status=status; } //+------------------------------------------------------------------+ //| SetOperation | //+------------------------------------------------------------------+ void CEvolution::SetOperation(CObject *operation) { m_operations.Add(operation); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| ExpertSimpleRandom.mq5 | //| Copyright © 2013, Jordi Bassagaсas | //+------------------------------------------------------------------+ #include <Trade\SymbolInfo.mqh> #include <Arrays\ArrayObj.mqh> //+------------------------------------------------------------------+ //| CGrapic Class | //+------------------------------------------------------------------+ class CGraphic { protected: ENUM_TIMEFRAMES m_period; // Graphic's timeframe string m_pair; // Graphic's pair CSymbolInfo* m_symbol; // CSymbolInfo object CArrayObj* m_bars; // Array of bars public: //--- Constructor and destructor methods CGraphic(string pair); ~CGraphic(void); //--- Getter methods string GetPair(void); CSymbolInfo *GetSymbol(void); CArrayObj *GetBars(void); //--- Setter methods void SetPair(string pair); void SetSymbol(CSymbolInfo *symbol); void SetBar(CObject *bar); }; //+------------------------------------------------------------------+ //| Constuctor | //+------------------------------------------------------------------+ CGraphic::CGraphic(string pair) { m_period=PERIOD_M1; m_pair=pair; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CGraphic::~CGraphic(void) { } //+------------------------------------------------------------------+ //| GetPair | //+------------------------------------------------------------------+ string CGraphic::GetPair(void) { return m_pair; } //+------------------------------------------------------------------+ //| GetSymbol | //+------------------------------------------------------------------+ CSymbolInfo *CGraphic::GetSymbol(void) { return m_symbol; } //+------------------------------------------------------------------+ //| GetBars | //+------------------------------------------------------------------+ CArrayObj *CGraphic::GetBars(void) { return m_bars; } //+------------------------------------------------------------------+ //| SetPair | //+------------------------------------------------------------------+ void CGraphic::SetPair(string pair) { m_pair=pair; } //+------------------------------------------------------------------+ //| SetSymbol | //+------------------------------------------------------------------+ void CGraphic::SetSymbol(CSymbolInfo *symbol) { m_symbol=symbol; } //+------------------------------------------------------------------+ //| SetBar | //+------------------------------------------------------------------+ void CGraphic::SetBar(CObject *bar) { m_bars.Add(bar); } //+------------------------------------------------------------------+
3 Backtesting von ExpertSimpleRandom.mq5
Dieses zufällige Handelssystem hat eindeutig gezeigt, dass es für bestimmte Stop-Loss- und Take-Profit-Levels absolut geeignet ist. Allerdings gilt natürlich, dass die siegreichen SL/TP-Intervalle nicht für alle Symbole identisch sind. Das liegt ganz einfach daran, dass ein jedes Symbol zu einer jeden Zeit über eine eigene Persönlichkeit verfügt, oder anders gesagt: Alle Währungspaare bewegen sich in Bezug auf all die anderen vollkommen unterschiedlich. Identifizieren Sie also bitte zunächst diese Levels per Backtest, bevor Sie ExpertSimpleRandom.mq5 in einer realen Umgebung ausführen.
Ich werde Ihnen an dieser Stelle einige Daten zur Verfügung stellen, mit denen die in diesem Artikel behandelte Idee nichts anderes als siegreich hervorgehen wird. Zu dieser Überzeugung kommt man, wenn man ExpertSimpleRandom.mq5 viele Male durch den MetaTrader-5-Strategietester laufen lässt.
Einige siegreiche Eingabedaten für EURUSD - Januar 2012 sind u.a.:
- StopLoss: 400
- TakeProfit: 600
- LotGröße 0.01
- Dauer: MONAT
Nummer 1 ausführen:
Nummer 2 ausführen:
Nummer 3 ausführen:
Fazit
Wir haben nun also gelernt, wie wir Prinzipien objektorientierter Programmierung auf unser Handelssystem anwenden. Hierfür müssen wir zuerst eine mechanische Handelsstrategie definieren. Unserer Handelsidee war dabei nicht wirklich schwer zu verstehen: „Kurze Trends eines volatilen Marktes sind nahezu zufällig“. Dieses Ergebnis wurde bereits von einigen Experten unter einigen Umständen beobachtet.
Gleich danach haben wir angefangen, unseren EA als eine Art Lebensform zu betrachten. Dank dieser Annahme kamen wir zu dem Schluss, dass unser Forex-Roboter im Großen und Ganzen aus drei Teilen besteht: einem Gehirn, einem Chart und aus etwas, das wir Evolution nennen.
Und schließlich haben wir noch das System eines EAs programmiert, das eine für einen Backtest geeignete Logik besitzt, haben diesen Roboter viele Male den Januar 2012-Test absolvieren lassen. Wir kamen zu dem Schluss, dass das System meistens gewinnt. Die Idee hinter diesem System hat sich also als wahr, allerdings - aufgrund ihrer Simplizität - als nicht besonders effektiv erwiesen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/703
![Wie Sie OpenCl öffnen und für Kalkulationen verwenden.](https://c.mql5.com/2/0/avatar__4.png)
![Creating Neural Network EAs Using MQL5 Wizard and Hlaiman EA Generator](https://c.mql5.com/2/0/HLAIMAN.png)
![MQL5-Kochbuch: Umgang mit typischen Chartereignissen](https://c.mql5.com/2/11/OnChartEvent_MetaTrader5.png)
![Einen automatisierten News-Trader kreieren](https://c.mql5.com/2/0/cover.png)
![MQL5 - Sprache von Handelsstrategien, eingebaut ins Kundenterminal MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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.