
Der MQL5 Assistent: Wie man ein Modul an Handelssignalen erzeugt
Einleitung
MetaTrader 5 besitzt ein leistungsstarkes Tool zum schnellen Überpüfen von Handelskonzepten, nämlich den Handelsstrategien-Generator des MQL5 Assistenten. Die Verwendung des MQL5 Assistenten zur automatischen Erzeugung von Expert Advisor Codes wird im Beitrag "Der MQL5 Assistent: Erzeugung von Expert Advisors ohne Programmieren" beschrieben. Das offene System zur Generierung des Codes erlaubt Ihnen, die Standardklassen durch Ihre eigenen Klassen an Handelssignalen, Geldverwaltungssystemen und nachziehenden Modulen zu ergänzen.
Dieser Beitrag beschreibt die Prinzipien des Schreibens von Handelssignal-Modulen und wie man sie bei der Erzeugung von Expert Advisors mit dem MQL5 Assistenten einsetzt.
Der mit dem MQL5 Assistenten erzeugte Expert Advisor ruht auf vier Säulen - den vier Basisklassen:
Abb. 1 Die Struktur der CExpert Basisklasse
Die CExpert Klasse (oder ihre Unterklasse) ist der "Hauptmotor" eines Handelsroboters. Eine Instanz von CExpert enthält eine Kopie jeder der folgenden Klasse: CExpertSignal, CExpertMoney und CExpertTrailing (oder ihre Unterklassen):
- CExpertSignal ist die Basis des Handelssignale-Generators. Eine Instanz der in CExpert umfassten, abgeleiteten CExpertSignal Klasse versorgt einen Expert Advisor mit Informationen auf Grundlage des eingebauten Algorithmus über die Möglichkeit eines Markteintritts, die Eintrittslevel und die Platzierung von schützenden Orders. Die endgültige Entscheidung über die Ausführung der Handelsoperationen trifft der EA.
- CExpertMoney ist die Basis des Geld- und Risikoverwaltungssystems. Eine Instanz der abgeleiteten CExpertMoney Klasse berechnet die Volumen zur Eröffnung von Positions und der Platzierung von pending Orders. Die endgültige Entscheidung über das Volumen trifft der EA.
- CExpertTrailing - ist die Basis des Moduls zur Unterstützung offener Positions. Eine Instanz der abgeleiteten CExpertTrailing Klasse informiert einen EA über die Notwendigkeit schützende Orders einer Position modifizieren zu müssen. Die endgültige Entscheidung über eine Order-Modifikation trifft der EA.
Zusätzlich dazu sind die Mitglieder der CExpert Klasse Instanzen der folgenden Klassen:
- CExpertTrade (für den Handel)
- CIndicators (zur Kontrolle der Indikatoren und Zeitreihen, die an der Arbeit des EA beteiligt sind).
- CSymbolInfo (zum Abfragen von Informationen über das Instrument)
- CAccountInfo (zum Abfragen von Informationen über den Status des Handels-Accounts)
- CPositionInfo (zum Abfragen von Informationen über Positions)
- COrderInfo (zum Abfragen von Informationen über pending Orders)
Im Folgenden bezeichnen wir daher mit "Expert" eine Instanz von CExpert oder ihrer Unterklasse.
Weitere Einzelheiten zu CExpert und der Arbeit mit dieser Klasse werden in einem separaten Beitrag beschrieben.
1. CExpertSignal Basisklasse
CExpertSignal ist die Basis des Handelssignale-Generators. Zur Kommunikation mit "außen" besitzt CExpertSignal eine Reihe von public virtuellen Methoden:
Initialisierung | Beschreibung |
virtuell Init | Initialisierung der Klasseninstanz bietet die Synchronisierung der Moduldaten mit den EA-Daten |
virtuell ValidationSettings | Validierung der eingerichteten Parameter |
virtuell InitIndicators | Erzeugt und initialisiert alle Indikatoren und Zeitreihen, die für die Arbeit mit Handelssignalen notwendig sind |
Signale zur Eröffnung/Umkehrung/Schließen von Positions |
|
virtuell CheckOpenLong | Generiert das Signal zur Eröffnung einer Long Position, legt die Eintrittslevels fest und platziert schützende Order |
virtuell CheckOpenShort | Generiert das Signal zur Eröffnung einer Short Position, legt die Eintrittslevels fest und platziert schützende Order |
virtuell CheckCloseLong | Generiert das Signal zum Schließen einer Long Position und legt das Austrittslevel |
virtuell CheckCloseShort | Generiert das Signal zum Schließen einer Short Position und legt das Austrittslevel |
virtuell CheckReverseLong | Generiert das Signal zur Umkehr einer Long Position, legt die Umkehrlevels fest und platziert schützende Order |
virtuell CheckReverseShort | Generiert das Signal zur Umkehr einer Short Position, legt die Umkehrlevels fest und platziert schützende Order |
Verwaltung von pending Orders |
|
virtuell CheckTrailingOrderLong | Generiert das Signal zur Modifizierung einer pending Buy-Order und legt den neuen Order-Preis fest |
virtuell CheckTrailingOrderShort | Generiert das Signal zur Modifizierung einer pending Sell-Order und legt den neuen Order-Preis fest |
Beschreibung der Methoden
1.1 Initialisierungsmethoden:
Die Init() Methode wird gleich, nachdem dem Expert eine Klasseninstanz hinzugefügt wurde, automatisch aufgerufen. Aufhebung der Methode ist nicht notwendig.
virtual bool Init(CSymbolInfo* symbol, ENUM_TIMEFRAMES period, double adjusted_point);
Die ValidationSettings() Methode wird direkt vom Expert aufgerufen, nachdem alle Parameter eingerichtet sind. Sollte es irgendwelche Einrichtungs-Parameter geben, muss diese Methode aufgehoben werden.
virtual bool ValidationSettings();
Sind alle Optionen gültig (d.h. es kann mit ihnen gearbeitet werden), muss die aufgehobene Methode 'true' liefern. Ist mind. einer der Parameter nicht korrekt, muss sie 'false' liefern (weitere Arbeit ist nicht möglich).
Die BasisklasseCExpertSignal besitzt keine anpassbaren Parameter, daher liefert diese Basisklassen-Methode immer 'true', ohne dabei Prüfungen durchzuführen.
Die InitIndicators () Methode implementiert die Erzeugung und Initialisierung aller notwendigen Indikatoren und Zeitreihen. Sie wird vom Expert aufgerufen, nachdem alle Parameter eingerichtet sind und ihre Exaktheit erfolgreich bestätigt wurde. Diese Methode sollte aufgehoben werden, wenn der Handelssignale-Generator mind. einen Indikator oder Zeitreihe verwendet.
virtual bool InitIndicators(CIndicators* indicators);
Indikatoren und/oder Zeitreihen sollten mittels der geeigneten Klassen der Standard Library verwendet werden. Der Sammlung an Indikatoren eines Experts sollten Zeiger aller Indikatoren und/oder Zeitreihen (ein Zeiger, der an sie als Parameter übertragen wird) hinzugefügt werden .
Die aufgehobene Methode muss 'true' liefern, wenn alle Arbeiten an den Indikatoren und/oder Zeitreihen erfolgreich waren (wenn also mit ihnen gearbeitet werden kann). Sollte mind. einer der Vorgänge mit den Indikatoren und/oder Zeitreihen fehlgeschlagen sein, muss die Methode 'false' liefern (weitere Arbeit ist nicht möglich).
Die Basisklasse CExpertSignal verwendet keine Indikatoren oder Zeitreihen, sodass ihre Methode immer 'true' liefert, ohne irgendeine Handlung auszuführen.
1.2 Methoden zur Prüfung des Signals zur Eröffnung von Positions:
Die CheckOpenLong() Methode generiert ein Signal zur Eröffnung einer Long Position und legt das Eintrittslevel und die Levels der Platzierung von schützenden Orders fest. Sie wird von einem Expert zur Feststellung aufgerufen, ob die Eröffnung einer Long Position notwendig ist. Die Methode muss aufgehoben werden, wenn mit der Generierung eines Signals zur Eröffnung einer Long Position gerechnet wird.
virtual bool CheckOpenLong(double& price, double& sl, double& tp, datetime& expiration);
Die Methode sollte den Algorithmus zur Prüfung der Bedingung zur Eröffnung einer Long Position implementieren. Wird diese Bedingung erfüllt, müssen dem Preis der Variablen, Stop Loss, Take Profit und dem Ablauf (Verweise, die an sie als Parameter übertragen werden), entsprechende Werte zugewiesen werden, und die Methode sollte 'true' liefern. Wird die Bedingung nicht erfüllt, muss die Methode 'false' liefern.
Die Basisklasse CExpertSignal besitzt keinen eingebauten Algorithmus zur Generierung eines Signals zur Eröffnung einer Long Position, sodass ihre Methode immer 'false' liefert.
Die CheckOpenShort() Methode generiert ein Signal zur Eröffnung einer Short Position und legt das Eintrittslevel und die Levels der Platzierung von schützenden Orders fest. Sie wird von einem Expert zur Feststellung aufgerufen, ob die Eröffnung einer Short Position notwendig ist. Die Methode muss aufgehoben werden, wenn mit der Generierung eines Signals zur Eröffnung einer Short Position gerechnet wird.
virtual bool CheckOpenShort(double& price, double& sl, double& tp, datetime& expiration);
Die Methode muss den Algorithmus zur Prüfung der Bedingung zur Eröffnung einer Short Position implementieren. Wird diese Bedingung erfüllt, müssen dem Preis der Variablen, Stop Loss, Take Profit und dem Ablauf (Verweise, die an sie als Parameter übertragen werden), entsprechende Werte zugewiesen werden, und die Methode sollte 'true' liefern. Wird die Bedingung nicht erfüllt, muss die Methode 'false' liefern.
Die Basisklasse CExpertSignal besitzt keinen eingebauten Algorithmus zur Generierung eines Signals zur Eröffnung einer Short Position, sodass ihre Methode immer 'false' liefert.
1.3 Methoden zur Prüfung des Signals zum Schließen von Positions:
Die CheckCloseLong() Methode generiert ein Signal zum Schließen einer Long Position und legt das Austrittslevel fest. Sie wird von einem Expert zur Feststellung aufgerufen, ob das Schließen einer Long Position notwendig ist. Die Methode muss aufgehoben werden, wenn mit der Generierung eines Signals zum Schließen einer Long Position gerechnet wird.
virtual bool CheckCloseLong(double& price);
Die Methode muss den Algorithmus zur Prüfung der Bedingung zum Schließen der Long Position implementieren. Wird diese Bedingung erfüllt, muss dem Preis der Variablen (Verweise, die an sie als Parameter übertragen werden) der entsprechende Werte zugewiesen werden, und die Methode sollte 'true' liefern. Wird die Bedingung nicht erfüllt, muss die Methdoe 'false' liefern.
Die Basisklasse CExpertSignal besitzt keinen eingebauten Algorithmus zur Generierung eines Signals zum Schließen der Long Position, sodass ihre Methode immer 'false' liefert.
Die CheckCloseShort() Methode generiert ein Signal zum Schließen einer Short Position und legt das Austrittslevel fest. Sie wird von einem Expert zur Feststellung aufgerufen, ob das Schließen einer Short Position notwendig ist. Die Methode muss aufgehoben werden, wenn mit der Generierung eines Signals zum Schließen einer Short Position gerechnet wird.
virtual bool CheckCloseShort(double& price);
Die Methode muss den Algorithmus zur Prüfung der Bedingung zum Schließen der Short Position implementieren.. Wird diese Bedingung erfüllt, muss dem Preis der Variablen (Verweise, die an sie als Parameter übertragen werden) der entsprechende Werte zugewiesen werden, und die Methode sollte 'true' liefern. Wird die Bedingung nicht erfüllt, muss die Methode 'false' liefern.
Die Basisklasse CExpertSignal besitzt keinen eingebauten Algorithmus zur Generierung eines Signals zum Schließen der Short Position, sodass ihre Methode immer 'false' liefert.
1.4. Methoden zur Prüfung des Signals zur Umkehrung von Positions:
Die CheckReverseLong Methode generiert ein Signal zur Umkehrung einer Long Position, legt das Umkehrlevel und die Levels der Platzierung von schützenden Orders fest. Sie wird von einem Expert zur Feststellung aufgerufen, ob die Umkehrung einer Long Position notwendig ist. Die Methode muss aufgehoben werden, wenn mit der Generierung eines Signals zur Umkehrung einer Long Position gerechnet wird.
virtual bool CheckReverseLong(double& price, double& sl, double& tp, datetime& expiration);
Die Methode muss den Algorithmus zur Prüfung der Bedingung für eine Umkehrung der Long Position implementieren. Wird diese Bedingung erfüllt, müssen dem Preis der Variablen, Stop Loss, Take Profit und dem Ablauf (Verweise, die an sie als Parameter übertragen werden), entsprechende Werte zugewiesen werden, und die Methode sollte 'true' liefern. Wird die Bedingung nicht erfüllt, muss die Methode 'false' liefern.
In der Basisklasse CExpertSignal ist der folgende Algorithmus zur Generierung eines Signals zur Umkehrung einer Long Position implementiert:
- Suche nach einem Signal zum Schließen einer Long Position.
- Suche nach einem Signal zum Öffnen einer Short Position.
- Sind beide Signale aktiv (die Bedingungen erfüllt) und stimmen der Schluss- und Eröffnungskurs überein, werden dem Preis der Variablen, Stop Loss, Take Profit und dem Ablauf (Verweise, die an sie als Parameter übertragen werden), entsprechende Werte zugewiesen werden, und die Methode liefert 'true'.
Die CheckReverseShort Methode generiert ein Signal zur Umkehrung einer Short Position, legt das Umkehrlevel und die Levels der Platzierung von schützenden Orders fest. Sie wird von einem Expert zur Feststellung aufgerufen, ob die Umkehrung einer Short Position notwendig ist. Die Methode muss aufgehoben werden, wenn mit der Generierung eines Signals zur Umkehrung einer Short Position auf Grundlage des Algorithmus gerechnet wird, der sich von dem in der Basisklasse implementierten unterscheidet.
virtual bool CheckReverseShort(double& price, double& sl, double& tp, datetime& expiration);
Die Methode muss den Algorithmus zur Prüfung der Bedingung für eine Umkehrung der Short Position implementieren. Wird diese Bedingung erfüllt, müssen dem Preis der Variablen, Stop Loss, Take Profit und dem Ablauf (Verweise, die an sie als Parameter übertragen werden), entsprechende Werte zugewiesen werden, und die Methode sollte 'true' liefern. Wird die Bedingung nicht erfüllt, muss die Methode 'false' liefern.
In der Basisklasse CExpertSignal ist der folgende Algorithmus zur Generierung eines Signals zur Umkehrung einer Short Position implementiert:
- Suche nach einem Signal zum Schließen einer Short Position
- Suche nach einem Signal zum Öffnen einer Long Position.
- Sind beide Signale aktiv (die Bedingungen erfüllt) und stimmen der Schluss- und Eröffnungskurs überein, werden dem Preis der Variablen, Stop Loss, Take Profit und dem Ablauf (Verweise, die an sie als Parameter übertragen werden), entsprechende Werte zugewiesen werden, und die Methode liefert 'true'.
Wird die Bedingung nicht erfüllt, liefert die Methode 'false'.
1.5. Methoden zur Prüfung des Signals zur Modifikation einer pending Order:
Die CheckTrailingOrderLong() Methode generiert ein Signal zur Modifikation einer pending Buy-Order und legt einen neuen Orderpreis fest. Sie wird von einem Expert zur Feststellung aufgerufen, ob die Modifikation einer pending Buy-Order notwendig ist. Die Methode muss aufgehoben werden, wenn mit der Generierung eines Signals zur Modifikation einer pending Buy-Order gerechnet wird.
virtual bool CheckTrailingOrderLong(COrderInfo* order, double& price)
Die Methode muss den Algorithmus zur Prüfung der Bedingung für eine Modifikation einer pending Buy-Order implementieren. Wird diese Bedingung erfüllt, muss dem Preis der Variable (der Verweis, der an sie als Parameter übertragen wird), der entsprechende Wert zugewiesen werden, und die Methode sollte 'true' liefern. Wird die Bedingung nicht erfüllt, muss die Methode 'false' liefern.
Die Basisklasse CExpertSignal besitzt keinen eingebauten Algorithmus zur Generierung eines Signals zur Modifikation einer pending Buy-Order, sodass ihre Methode immer 'false' liefert.
The CheckTrailingOrderShort() Methode generiert ein Signal zur Modifikation einer pending Sell-Order und legt einen neuen Orderpreis fest. Sie wird von einem Expert zur Feststellung aufgerufen, ob die Modifikation einer pending Sell-Order notwendig ist. Die Methode muss aufgehoben werden, wenn mit der Generierung eines Signals zur Modifikation einer pending Sell-Order gerechnet wird.
virtual bool CheckTrailingOrderShort(COrderInfo* order, double& price)
Die Methode muss den Algorithmus zur Prüfung der Bedingung für eine Modifikation einer pending Sell-Order implementieren. Wird diese Bedingung erfüllt, muss dem Preis der Variable (der Verweis, der an sie als Parameter übertragen wird), der entsprechende Wert zugewiesen werden, und die Methode sollte 'true' liefern. Wird die Bedingung nicht erfüllt, muss die Methode 'false' liefern.
Die Basisklasse CExpertSignal besitzt keinen eingebauten Algorithmus zur Generierung eines Signals zur Modifikation einer pending Sell-Order, sodass ihre Methode immer 'false' liefert.
2. Entwicklung Ihres eigenen Handelssignale-Generators
Da wir uns jetzt die Struktur der CExpertSignal Basisklasse angesehen haben, können Sie mit der Erzeugung Ihres eigenen Handelssignale-Generators beginnen.
Wie oben bereits ausgeführt, ist die CExpertSignal Klasse eine Reihe an virtuellen public "Hilfsseilen" - Methoden, mit deren Hilfe der Expert die Meinung des Handelssignale-Generators bzgl. des Markteintritts in die eine oder andere Richtung erkennen kann.
Daher ist unser erstes Ziel die Erzeugung unserer eigenen Klasse des Handelssignale-Generators, die von der CExpertSignal Klasse abgeleitet wird und die Aufhebung der geeigneten virtuellen Methoden und Implementierung der erforderlichen Algorithmen.
Unser zweites Ziel (ganz genauso wichtig) ist, diese Klasse für den MQL5 Assistenten auch "sichtbar" zu machen. Doch der Reihe nach..
2.1 Erzeugung der Klasse des Handelssignale-Generators
Also los.
Zuerst erzeugen wir (z.B. indem wir denselben MQL5 Assistenten verwenden) eine einzuschließende Datei mit der Erweiterung .mqh.
Im Dateimenü wählen wir dazu "Erzeugen" aus (oder drücken Ctrl+N) und geben die Erzeugung einer eingeschlossenen Datei an:
Abb. 2 Erzeugung einer einzuschließenden Datei mit Hilfe des MQL5 Assistenten.
An dieser Stelle möchte ich darauf hinweisen, dass diese Datei, damit sie vom MQL5 Assistenten als Signal-Generator "entdeckt" werden kann, im Include\Expert\Signal\ Order erzeugt werden sollte.
Um die Standard Librarynicht zuzumüllen, erzeugen Sie Ihren eigenen Include\Expert\Signal\MySignals Ordner, in dem Sie dann die SampleSignal.mqh Datei und die Parameter im MQL5 Assistenten anlegen:
Abb. 3 Standort der einzuschließenden Datei einrichten
Als Ergebnis der Arbeit des MQL5 Assistent erhalten wir dann das folgenden Muster:
//+------------------------------------------------------------------+ //| SampleSignal.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ // #define MacrosHello "Hello, world!" // #define MacrosYear 2010 //+------------------------------------------------------------------+ //| DLL imports | //+------------------------------------------------------------------+ // #import "user32.dll" // int SendMessageA(int hWnd,int Msg,int wParam,int lParam); // #import "my_expert.dll" // int ExpertRecalculate(int wParam,int lParam); // #import //+------------------------------------------------------------------+ //| EX5 imports | //+------------------------------------------------------------------+ // #import "stdlib.ex5" // string ErrorDescription(int error_code); // #import //+------------------------------------------------------------------+
Was nun folgt ist nur "manuelle" Arbeit. Entfernen Sie die unnötigen Teile entfernen und fügen, alles was benötigt wird, hinzu (Umfassen der Datei ExpertSignal.mqh der Standard Library und einer Klassenbeschreibung, die jetzt leer ist).
//+------------------------------------------------------------------+ //| SampleSignal.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| include files | //+------------------------------------------------------------------+ #include <Expert\ExpertSignal.mqh> //+------------------------------------------------------------------+ //| The CSampleSignal class. | //| Purpose: Class of trading signal generator. | //| It is derived from the CExpertSignal class. | //+------------------------------------------------------------------+ class CSampleSignal : public CExpertSignal { }; //+------------------------------------------------------------------+
Als nächstes müssen wir die Algorithmen auswählen..
Als Basis für unseren Handelssignale-Generator, ziehen wir das weit verbreite Modell "Preis übersteigt den gleitenden Durchschnitt" heran. Doch wir fügen noch eine Prämisse hinzu: "Nach Überschreiten des gleitenden Durchschnitts, bewegt sich der Preis zurück und geht erst dann in die richtige Richtung." Dies sollte unsere Datei abbilden.
Allgemeiner Tipp: Wenn Sie etwas schreiben, dann sparen Sie bitte nicht an Kommentaren, denn wenn Sie später nach einiger Zeit einen sauber kommentierten Code lesen, dann werden Sie sich riesig freuen.
//+------------------------------------------------------------------+ //| SampleSignal.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| include files | //+------------------------------------------------------------------+ #include <Expert\ExpertSignal.mqh> //+------------------------------------------------------------------+ //| Class CSampleSignal. | //| Purpose: Class of trading signal generator when price | //| crosses moving average, | //| entering on the subsequent back movement. | //| It is derived from the CExpertSignal class. | //+------------------------------------------------------------------+ class CSampleSignal : public CExpertSignal { }; //+------------------------------------------------------------------+
Jetzt müssen wir festlegen welche Daten für die Entscheidungen über die Erzeugung von Handelssignalen notwendig sind. In unserem Fall: der Eröffnungs- und der Schlusskurs des vorherigen Balkens und der Wert des gleitenden Durchschnitts auf dem gleichen, vorherigen Balken.
Um auf diese Daten zugreifen zu können, verwenden wir die Stadnard-Library Klassen CiOpen, CiClose und CiMA. Zu den Indikatoren und Zeitreihen kommen wir später noch.
Legen wir in der Zwischenzeit lieber eine Liste der Einstellungen für unseren Generator fest. Zuerst müssen wir den gleitenden Durchschnitt festlegen. Diese Parameter beinhalten den Zeitraum, die Verschiebung entlang der Zeitachse, die Durchschnittsmethode und das Objekt des Durchschnitts. Danach müssen wir das Einstiegslevel und die Level zur Platzierung von schützenden Orders einrichten sowie die Dauer einer pending Order, da wir ja mit pending Orders arbeiten werden.
Alle Einstellungen des Generators werden in geschützten Datenmitgliedern der Klasse abgelegt. Zugriff auf die Einstellungen wird mit Hilfe der geeigneten public Methoden implementiert.
Wir ergänzen unsere Datei mit diesen Veränderungen:
//+------------------------------------------------------------------+ //| SampleSignal.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| include files | //+------------------------------------------------------------------+ #include <Expert\ExpertSignal.mqh> //+------------------------------------------------------------------+ //| The CSampleSignal class. | //| Purpose: Class of trading signal generator when price | //| crosses moving average, | //| entering on the subsequent back movement. | //| It is derived from the CExpertSignal class. | //+------------------------------------------------------------------+ class CSampleSignal : public CExpertSignal { protected: //--- Setup parameters int m_period_ma; // averaging period of the MA int m_shift_ma; // shift of the MA along the time axis ENUM_MA_METHOD m_method_ma; // averaging method of the MA ENUM_APPLIED_PRICE m_applied_ma; // averaging object of the MA double m_limit; // level to place a pending order relative to the MA double m_stop_loss; // level to place a stop loss order relative to the open price double m_take_profit; // level to place a take profit order relative to the open price int m_expiration; // lifetime of a pending order in bars public: //--- Methods to set the parameters void PeriodMA(int value) { m_period_ma=value; } void ShiftMA(int value) { m_shift_ma=value; } void MethodMA(ENUM_MA_METHOD value) { m_method_ma=value; } void AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value; } void Limit(double value) { m_limit=value; } void StopLoss(double value) { m_stop_loss=value; } void TakeProfit(double value) { m_take_profit=value; } void Expiration(int value) { m_expiration=value; } }; //+------------------------------------------------------------------+
Da wir mit geschützten Datenmitgliedern arbeiten, müssen wir einen Klassen-Constructor hinzufügen, in dem wir diese Daten per Standardwerte initialisieren.
Um die Parameter prüfen zu können, heben wir die virtuelle Methode ValidationSettings der gemäß der Beschreibung der Basisklasse auf.
Beschreibung der 'Klasse:
class CSampleSignal : public CExpertSignal { protected: //--- Setup parameters int m_period_ma; // averaging period of the MA int m_shift_ma; // shift of the MA along the time axis ENUM_MA_METHOD m_method_ma; // averaging method of the MA ENUM_APPLIED_PRICE m_applied_ma; // averaging object of the MA double m_limit; // level to place a pending order relative to the MA double m_stop_loss; // level to place a stop loss order relative to the open price double m_take_profit; // level to place a take profit order relative to the open price int m_expiration; // lifetime of a pending order in bars public: CSampleSignal(); //--- Methods to set the parameters void PeriodMA(int value) { m_period_ma=value; } void ShiftMA(int value) { m_shift_ma=value; } void MethodMA(ENUM_MA_METHOD value) { m_method_ma=value; } void AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value; } void Limit(double value) { m_limit=value; } void StopLoss(double value) { m_stop_loss=value; } void TakeProfit(double value) { m_take_profit=value; } void Expiration(int value) { m_expiration=value; } //--- Methods to validate the parameters virtual bool ValidationSettings(); };
Implementierung der ValidationSettings() Methode:
//+------------------------------------------------------------------+ //| Validation of the setup parameters. | //| INPUT: No. | //| OUTPUT: true if the settings are correct, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::ValidationSettings() { //--- Validation of parameters if(m_period_ma<=0) { printf(__FUNCTION__+": the MA period must be greater than zero"); return(false); } //--- ok return(true); } //+------------------------------------------------------------------+
Da wir jetzt mit den gesamten Vorbereitungsarbeiten fertig sind, können wir uns eingehender mit Indikatoren und Zeitreihen beschäftigen.
Indikatoren und Zeitreihen sind die Hauptinformationsquelle für den Entscheidungsprozess (klar können Sie auch eine Münze werfen oder nach den Mondphasen gehen, doch kann das nur schwer formal dargestellt werden).
Wie wir oben ja bereits festgelegt haben, brauchen wir für vernünftige Entscheidungen die folgenden Informationen: den Eröffnungskurs des vorherigen Balkens, der Schlusskurs des vorherigen Balkens und den Wert des gleitenden Durchschnitts auf demselben vorherigen Balken.
Um diese Daten zu bekommen, verwenden wir die folgenden Klassen der Standard Library:
- CiOpen - Zugriff auf den Eröffnungskurs des vorherigen Balkens,
- CiClose - Zugriff auf den Schlusskurs des vorherigen Balkens,
- CiMA - Zugriff auf den Wert des gleitenden Durchschnitts des vorherigen Balkens.
Sie fragen Sie vielleicht: "Warum verwendet man den Indikator oder Zeitreihen, die von einer Klasse 'umhüllt' sind, wenn man nur eine einzige Zahl erhalten möchte?"
Die Bedeutung liegt in bisschen im Verborgenen - und das mache ich Ihnen nun klar.
Wie werden die Daten eines Indikators oder von Zeitreihen verwendet?
Dazu müssen wir zunächst einen Indikator erzeugen.
Danach müssen wir die notwendige Datenmenge in einen Zwischenpuffer kopieren.
Und schließlich müssen wir überprüfen, ob der Kopiervorgang auch abgeschlossen ist.
Denn nur so können wir die Daten verwenden.
Wenn Sie die Klassen der Standard Library verwenden, sparen Sie sich die Notwendigkeit, einen Indikator erzeugen zu müssen und müssen sich auch keine Gedanken mach, ob Zwischenpuffer verfügbar sind oder sich mit Datenladen oder Freigabe eines Handels herumschlagen. Das Objekt einer geeigneten Klasse macht das alles für sie. Alle benötigten Indikatoren werden durch unseren Signal-Generator während der Initialisierungsphase generiert. Und alle Indikatoren kommen dann mit dem entsprechend notwendigen temporären Puffer. Und außerdem: sobald wir der Sammlung ein Indikator- oder Zeitreihenobjekt hinzufügen (das Objekte einer speziellen Klasse), müssen Sie sich über die Relevanz der Daten keine Sorgen mehr machen (der Expert aktualisiert die Daten automatisch).
Wir platzieren die Objekte dieser Klassen in die geschützten Datenmitglieder und erzeugen für jedes Objekt eine Methode zur Initialisierung und zum Datenzugriff.Dazu heben wir die virtuell Methode InitIndicators (gemäß der Beschreibung der Basisklasse) auf.
Beschreibung der Klasse:
class CSampleSignal : public CExpertSignal { protected: CiMA m_MA; // object to access the values om the moving average CiOpen m_open; // object to access the bar open prices CiClose m_close; // object to access the bar close prices //--- Setup parameters int m_period_ma; // averaging period of the MA int m_shift_ma; // shift of the MA along the time axis ENUM_MA_METHOD m_method_ma; // averaging method of the MA ENUM_APPLIED_PRICE m_applied_ma; // averaging object of the MA double m_limit; // level to place a pending order relative to the MA double m_stop_loss; // level to place a stop loss order relative to the open price double m_take_profit; // level to place a take profit order relative to the open price int m_expiration; // lifetime of a pending order in bars public: CSampleSignal(); //--- Methods to set the parameters void PeriodMA(int value) { m_period_ma=value; } void ShiftMA(int value) { m_shift_ma=value; } void MethodMA(ENUM_MA_METHOD value) { m_method_ma=value; } void AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value; } void Limit(double value) { m_limit=value; } void StopLoss(double value) { m_stop_loss=value; } void TakeProfit(double value) { m_take_profit=value; } void Expiration(int value) { m_expiration=value; } //--- Method to validate the parameters virtual bool ValidationSettings(); //--- Method to validate the parameters virtual bool InitIndicators(CIndicators* indicators); protected: //--- Object initialization method bool InitMA(CIndicators* indicators); bool InitOpen(CIndicators* indicators); bool InitClose(CIndicators* indicators); //--- Methods to access object data double MA(int index) { return(m_MA.Main(index)); } double Open(int index) { return(m_open.GetData(index)); } double Close(int index) { return(m_close.GetData(index)); } };
Implementierung der Methoden InitIndicators, InitMA, InitOpen, InitClose:
//+------------------------------------------------------------------+ //| Initialization of indicators and timeseries. | //| INPUT: indicators - pointer to the object - collection of | //| indicators and timeseries. | //| OUTPUT: true in case of success, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::InitIndicators(CIndicators* indicators) { //--- Validation of the pointer if(indicators==NULL) return(false); //--- Initialization of the moving average if(!InitMA(indicators)) return(false); //--- Initialization of the timeseries of open prices if(!InitOpen(indicators)) return(false); //--- Initialization of the timeseries of close prices if(!InitClose(indicators)) return(false); //--- Successful completion return(true); } //+------------------------------------------------------------------+ //| Initialization of the moving average | //| INPUT: indicators - pointer to the object - collection of | //| indicators and timeseries. | //| OUTPUT: true in case of success, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::InitMA(CIndicators* indicators) { //--- Initialization of the MA object if(!m_MA.Create(m_symbol.Name(),m_period,m_period_ma,m_shift_ma,m_method_ma,m_applied_ma)) { printf(__FUNCTION__+": object initialization error"); return(false); } m_MA.BufferResize(3+m_shift_ma); //--- Adding an object to the collection if(!indicators.Add(GetPointer(m_MA))) { printf(__FUNCTION__+": object adding error"); return(false); } //--- Successful completion return(true); } //+------------------------------------------------------------------+ //| Initialization of the timeseries of open prices. | //| INPUT: indicators - pointer to the object - collection of | //| indicators and timeseries. | //| OUTPUT: true in case of success, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::InitOpen(CIndicators* indicators) { //--- Initialization of the timeseries object if(!m_open.Create(m_symbol.Name(),m_period)) { printf(__FUNCTION__+": object initialization error"); return(false); } //--- Adding an object to the collection if(!indicators.Add(GetPointer(m_open))) { printf(__FUNCTION__+": object adding error"); return(false); } //--- Successful completion return(true); } //+------------------------------------------------------------------+ //| Initialization of the timeseries of close prices. | //| INPUT: indicators - pointer to the object - collection of | //| indicators and timeseries. | //| OUTPUT: true in case of success, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::InitClose(CIndicators* indicators) { //--- Initialization of the timeseries object if(!m_close.Create(m_symbol.Name(),m_period)) { printf(__FUNCTION__+": object initialization error"); return(false); } //--- Adding an object to the collection if(!indicators.Add(GetPointer(m_close))) { printf(__FUNCTION__+": object adding error"); return(false); } //--- Successful completion return(true); } //+------------------------------------------------------------------+
Nun ist die gesamten Vorbereitungsarbeit abgeschlossen. Und Sie sehen, dass unsere Klasse ganz erheblich angewachsen ist.
Jetzt können wir endlich Handelssignale erzeugen.
Abb. 4 Handelssignale für den Preis, der den gleitenden Durchschnitt übersteigt
Sehen wir uns unsere Algorithmen nochmals genauer an.
1. Das Signal für Kauf erscheint wenn beim vorherigen Balken die folgenden Bedingungen erfüllt sind:
- der Eröffnungspreis des Balkens liegt unter dem Wert des gleitenden Durchschnitts,
- der Schlusspreis des Balkens liegt über dem Wert des gleitenden Durchschnitts,
- der gleitende Durchschnitt nimmt zu.
In diesem Fall bieten wir die Platzierung einer pending Buy-Order mit den durch die Einstellungen festgelegten Parameter an. Und dazu heben wir die virtuelle Methode CheckOpenLong auf und füllen sie mit den entsprechenden Funktionseinheiten.
2. Das Signal für Verkauf erscheint wenn beim vorherigen Balken die folgenden Bedingungen erfüllt sind:
- der Eröffnungspreis des Balkens liegt über dem Wert des gleitenden Durchschnitts,
- der Schlusspreis des Balkens liegt unter dem Wert des gleitenden Durchschnitts,
- der gleitende Durchschnitt nimmt ab.
In diesem Fall bieten wir die Platzierung einer pending Sell-Order mit den durch die Einstellungen festgelegten Parameter an.. Und dazu heben wir die virtuelle Methode CheckOpenShort auf und füllen sie mit den entsprechenden Funktionseinheiten.
3. Wir generieren keine Signale zum Schließen von Positions. Die Positions werden durch Stop Loss/Take Profit ордерам. geschlossen.
Und deshalb heben wir die virtuelle Methoden CheckCloseLong und CheckCloseShort auch nicht auf.
4. Wir schlagen die Modifikation einer pending Order entlang des gleitenden Durchschnitts in einem "Abstand" vor, der von den Einstellungen festgelegt wurde.
Dazu heben wir die virtuellen Methoden CheckTrailingOrderLong und CheckTrailingOrderShort auf, und füllen sie mit den entsprechenden Funktionseinheiten.
Beschreibung der Klasse:
class CSampleSignal : public CExpertSignal { protected: CiMA m_MA; // object to access the values of the moving average CiOpen m_open; // object to access the bar open prices CiClose m_close; // object to access the bar close prices //--- Setup parameters int m_period_ma; // averaging period of the MA int m_shift_ma; // shift of the MA along the time axis ENUM_MA_METHOD m_method_ma; // averaging method of the MA ENUM_APPLIED_PRICE m_applied_ma; // averaging object of the MA double m_limit; // level to place a pending order relative to the MA double m_stop_loss; // level to place a stop loss order relative to the open price double m_take_profit; // level to place a take profit order relative to the open price int m_expiration; // lifetime of a pending order in bars public: CSampleSignal(); //--- Methods to set the parameters void PeriodMA(int value) { m_period_ma=value; } void ShiftMA(int value) { m_shift_ma=value; } void MethodMA(ENUM_MA_METHOD value) { m_method_ma=value; } void AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value; } void Limit(double value) { m_limit=value; } void StopLoss(double value) { m_stop_loss=value; } void TakeProfit(double value) { m_take_profit=value; } void Expiration(int value) { m_expiration=value; } //--- Method to validate the parameters virtual bool ValidationSettings(); //--- Method to validate the parameters virtual bool InitIndicators(CIndicators* indicators); //--- Methods to generate signals to enter the market virtual bool CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration); virtual bool CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration); //--- Methods to generate signals of pending order modification virtual bool CheckTrailingOrderLong(COrderInfo* order,double& price); virtual bool CheckTrailingOrderShort(COrderInfo* order,double& price); protected: //--- Object initialization method bool InitMA(CIndicators* indicators); bool InitOpen(CIndicators* indicators); bool InitClose(CIndicators* indicators); //--- Methods to access object data double MA(int index) { return(m_MA.Main(index)); } double Open(int index) { return(m_open.GetData(index)); } double Close(int index) { return(m_close.GetData(index)); } };
Implementierung der Methoden CheckOpenLong, CheckOpenShort, CheckTrailingOrderLong, CheckTrailingOrderShort:
//+------------------------------------------------------------------+ //| Check whether a Buy condition is fulfilled | //| INPUT: price - variable for open price | //| sl - variable for stop loss price, | //| tp - variable for take profit price | //| expiration - variable for expiration time. | //| OUTPUT: true if the condition is fulfilled, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration) { //--- Preparing the data double spread=m_symbol.Ask()-m_symbol.Bid(); double ma =MA(1); double unit =PriceLevelUnit(); //--- Checking the condition if(Open(1)<ma && Close(1)>ma && ma>MA(2)) { price=m_symbol.NormalizePrice(ma-m_limit*unit+spread); sl =m_symbol.NormalizePrice(price-m_stop_loss*unit); tp =m_symbol.NormalizePrice(price+m_take_profit*unit); expiration+=m_expiration*PeriodSeconds(m_period); //--- Condition is fulfilled return(true); } //--- Condition is not fulfilled return(false); } //+------------------------------------------------------------------+ //| Check whether a Sell condition is fulfilled. | //| INPUT: price - variable for open price, | //| sl - variable for stop loss, | //| tp - variable for take profit | //| expiration - variable for expiration time. | //| OUTPUT: true if the condition is fulfilled, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration) { //--- Preparing the data double ma =MA(1); double unit=PriceLevelUnit(); //--- Checking the condition if(Open(1)>ma && Close(1)<ma && ma<MA(2)) { price=m_symbol.NormalizePrice(ma+m_limit*unit); sl =m_symbol.NormalizePrice(price+m_stop_loss*unit); tp =m_symbol.NormalizePrice(price-m_take_profit*unit); expiration+=m_expiration*PeriodSeconds(m_period); //--- Condition is fulfilled return(true); } //--- Condition is not fulfilled return(false); } //+------------------------------------------------------------------+ //| Check whether the condition of modification | //| of a Buy order is fulfilled. | //| INPUT: order - pointer at the object-order, | //| price - a variable for the new open price. | //| OUTPUT: true if the condition is fulfilled, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::CheckTrailingOrderLong(COrderInfo* order,double& price) { //--- Checking the pointer if(order==NULL) return(false); //--- Preparing the data double spread =m_symbol.Ask()-m_symbol.Bid(); double ma =MA(1); double unit =PriceLevelUnit(); double new_price=m_symbol.NormalizePrice(ma-m_limit*unit+spread); //--- Checking the condition if(order.PriceOpen()==new_price) return(false); price=new_price; //--- Condition is fulfilled return(true); } //+------------------------------------------------------------------+ //| Check whether the condition of modification | //| of a Sell order is fulfilled. | //| INPUT: order - pointer at the object-order, | //| price - a variable for the new open price. | //| OUTPUT: true if the condition is fulfilled, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::CheckTrailingOrderShort(COrderInfo* order,double& price) { //--- Checking the pointer if(order==NULL) return(false); //--- Preparing the data double ma =MA(1); double unit=PriceLevelUnit(); double new_price=m_symbol.NormalizePrice(ma+m_limit*unit); //--- Checking the condition if(order.PriceOpen()==new_price) return(false); price=new_price; //--- Condition is fulfilled return(true); } //+------------------------------------------------------------------+
Das erste Problem ist also gelöst. Der oben stehende Code ist ein Quellcode der Handelssignale-Generator Klasse, die unsere Hauptaufgabe erfüllt.
2.2 Vorbereitung der Beschreibung der erzeugten Handelssignale-Klasse für den MQL5 Assistenten
Wenden wir uns nun unserem zweiten Problem zu. Unser Signal sollte vom Generator der Handelsstrategien des MQL5 Assistenten auch "erkannt" werden.
Die erste dazu notwendige Bedingung haben wir schon geschaffen: wir haben die Datei, in der er vom MQL5 Assistenten "gefunden" werden kann, platziert. Doch das genügt noch nicht. Der MQL5 Assistent muss die Datei nicht nur "finden", er muss sie auch als solche "erkennen". Dazu müssen wir dem ursprünglichen Text den Klassen-Descriptor für den MQL5 Assistenten hinzufügen.
Einen Klassen-Descriptor ist ein Block an Kommentaren, der gemäß bestimmter Regeln zusammengesetzt ist.
Und die sehen wir uns jetzt mal an.
1. Der Kommentarblock sollte mit den folgenden Zeilen beginnen:
// wizard description start //+------------------------------------------------------------------+ //| Description of the class |
2. Die nächste Zeile ist ein Text-Descriptor (das was wir im MQL5 Assistenten dann sehen, wenn wir das Signal auswählen) im Format "//| Title=<Text> |". Ist der Text zu lang für einen Zeile, dann fügen Sie noch eine Zeile (aber nicht mehr!) hinzu.
In unserem Fall ergibt das dann:
//| Title=Signal on the crossing of a price and the MA | //| entering on its back movement |
3. Danach folgt einen Zeile mit dem spezifizierten Klassentyp im Format "//| Type=<Type> |". Das <Typ> Feld muss den Signalwert besitzen (zusätzlich zu Signalen kennt der MQL5 Assistent auch noch andere Typen an Klassen).
Man schreibt:
//| Type=Signal |
4. Die folgende Zeile im Format "//| Name=<Name> |" ist der Kurzname des Signals (den der MQL5 Assistent zur Generierung der Namen der globalen Variablen des Experts verwendet).
Wir erhalten folgendes:
//| Name=Sample |
5. Der Name einer Klasse ist ein wichtiges Element der Beschreibung. In der Zeile im Format "//| Class=<ClassNameа> |", muss der <ClassName> Parameter mit dem Namen unserer Klasse übereinstimmen:
//| Class=CSampleSignal |
6. Wir füllen diese Zeile nicht, doch muss sie trotzdem da sein (sie stellt die Verbindung zum Bereich Sprachreferenz dar):
//| Page= |
7. Des weiteren gibt es die Beschreibungen der Parameter für die Signaleinrichtung, die
aus einer Anzahl Reihen bestehen (ihre Anzahl = der Anzahl der Parameter).
Das Format jeder Zeile lautet: "//| Parameter=<NameOfMethode>,<TypeOfParameter>,<DefaultValue> |".
Und das ist unser Reihe an Parametern:
//| Parameter=PeriodMA,int,12 | //| Parameter=ShiftMA,int,0 | //| Parameter=MethodMA,ENUM_MA_METHOD,MODE_EMA | //| Parameter=AppliedMA,ENUM_APPLIED_PRICE,PRICE_CLOSE | //| Parameter=Limit,double,0.0 | //| Parameter=StopLoss,double,50.0 | //| Parameter=TakeProfit,double,50.0 | //| Parameter=Expiration,int,10 |
8. Der Kommentarblock sollte mit den folgenden Zeilen enden:
//+------------------------------------------------------------------+ // wizard description end
Fügen wir nun dem Quellcode den Descriptor hinzu.
//+------------------------------------------------------------------+ //| SampleSignal.mqh | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| include files | //+------------------------------------------------------------------+ #include <Expert\ExpertSignal.mqh> // wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Signal on crossing of the price and the MA | //| entering on the back movement | //| Type=Signal | //| Name=Sample | //| Class=CSampleSignal | //| Page= | //| Parameter=PeriodMA,int,12 | //| Parameter=ShiftMA,int,0 | //| Parameter=MethodMA,ENUM_MA_METHOD,MODE_EMA | //| Parameter=AppliedMA,ENUM_APPLIED_PRICE,PRICE_CLOSE | //| Parameter=Limit,double,0.0 | //| Parameter=StopLoss,double,50.0 | //| Parameter=TakeProfit,double,50.0 | //| Parameter=Expiration,int,10 | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| CSampleSignal class. | //| Purpose: Class of trading signal generator when price | //| crosses moving average, | //| entering on the subsequent back movement. | //| It is derived from the CExpertSignal class. | //+------------------------------------------------------------------+ class CSampleSignal : public CExpertSignal { protected: CiMA m_MA; // object to access the values of the moving average CiOpen m_open; // object to access the bar open prices CiClose m_close; // object to access the bar close prices //--- Setup parameters int m_period_ma; // averaging period of the MA int m_shift_ma; // shift of the MA along the time axis ENUM_MA_METHOD m_method_ma; // averaging method of the MA ENUM_APPLIED_PRICE m_applied_ma; // averaging object of the MA double m_limit; // level to place a pending order relative to the MA double m_stop_loss; // level to place a stop loss order relative to the open price double m_take_profit; // level to place a take profit order relative to the open price int m_expiration; // lifetime of a pending order in bars public: CSampleSignal(); //--- Methods to set the parameters void PeriodMA(int value) { m_period_ma=value; } void ShiftMA(int value) { m_shift_ma=value; } void MethodMA(ENUM_MA_METHOD value) { m_method_ma=value; } void AppliedMA(ENUM_APPLIED_PRICE value) { m_applied_ma=value; } void Limit(double value) { m_limit=value; } void StopLoss(double value) { m_stop_loss=value; } void TakeProfit(double value) { m_take_profit=value; } void Expiration(int value) { m_expiration=value; } //---Method to validate the parameters virtual bool ValidationSettings(); //--- Method to validate the parameters virtual bool InitIndicators(CIndicators* indicators); //--- Methods to generate signals to enter the market virtual bool CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration); virtual bool CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration); //--- Methods to generate signals of pending order modification virtual bool CheckTrailingOrderLong(COrderInfo* order,double& price); virtual bool CheckTrailingOrderShort(COrderInfo* order,double& price); protected: //--- Object initialization method bool InitMA(CIndicators* indicators); bool InitOpen(CIndicators* indicators); bool InitClose(CIndicators* indicators); //--- Methods to access object data double MA(int index) { return(m_MA.Main(index)); } double Open(int index) { return(m_open.GetData(index)); } double Close(int index) { return(m_close.GetData(index)); } }; //+------------------------------------------------------------------+ //| CSampleSignal Constructor. | //| INPUT: No. | //| OUTPUT: No. | //| REMARK: No. | //+------------------------------------------------------------------+ void CSampleSignal::CSampleSignal() { //--- Setting the default values m_period_ma =12; m_shift_ma =0; m_method_ma =MODE_EMA; m_applied_ma =PRICE_CLOSE; m_limit =0.0; m_stop_loss =50.0; m_take_profit=50.0; m_expiration =10; } //+------------------------------------------------------------------+ //| Validation of parameters. | //| INPUT: No. | //| OUTPUT: true if the settings are correct, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::ValidationSettings() { //--- Validation of parameters if(m_period_ma<=0) { printf(__FUNCTION__+": the MA period must be greater than zero"); return(false); } //--- Successful completion return(true); } //+------------------------------------------------------------------+ //| Initialization of indicators and timeseries. | //| INPUT: indicators - pointer to the object - collection of | //| indicators and timeseries. | //| OUTPUT: true in case of success, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::InitIndicators(CIndicators* indicators) { //--- Validation of the pointer if(indicators==NULL) return(false); //--- Initialization of the moving average if(!InitMA(indicators)) return(false); //--- Initialization of the timeseries of open prices if(!InitOpen(indicators)) return(false); //--- Initialization of the timeseries of close prices if(!InitClose(indicators)) return(false); //--- Successful completion return(true); } //+------------------------------------------------------------------+ //| Initialization of the moving average | //| INPUT: indicators - pointer to the object - collection of | //| indicators and timeseries. | //| OUTPUT: true in case of success, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::InitMA(CIndicators* indicators) { //--- Initialization of the MA object if(!m_MA.Create(m_symbol.Name(),m_period,m_period_ma,m_shift_ma,m_method_ma,m_applied_ma)) { printf(__FUNCTION__+": object initialization error"); return(false); } m_MA.BufferResize(3+m_shift_ma); //--- Adding an object to the collection if(!indicators.Add(GetPointer(m_MA))) { printf(__FUNCTION__+": object adding error"); return(false); } //--- Successful completion return(true); } //+------------------------------------------------------------------+ //| Initialization of the timeseries of open prices. | //| INPUT: indicators - pointer to the object - collection of | //| indicators and timeseries. | //| OUTPUT: true in case of success, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::InitOpen(CIndicators* indicators) { //--- Initialization of the timeseries object if(!m_open.Create(m_symbol.Name(),m_period)) { printf(__FUNCTION__+": object initialization error"); return(false); } //--- Adding an object to the collection if(!indicators.Add(GetPointer(m_open))) { printf(__FUNCTION__+": object adding error"); return(false); } //--- Successful completion return(true); } //+------------------------------------------------------------------+ //| Initialization of the timeseries of close prices. | //| INPUT: indicators - pointer to the object - collection of | //| indicators and timeseries. | //| OUTPUT: true in case of success, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::InitClose(CIndicators* indicators) { //--- Initialization of the timeseries object if(!m_close.Create(m_symbol.Name(),m_period)) { printf(__FUNCTION__+": object initialization error"); return(false); } //--- Adding an object to the collection if(!indicators.Add(GetPointer(m_close))) { printf(__FUNCTION__+": object adding error"); return(false); } //--- Successful completion return(true); } //+------------------------------------------------------------------+ //| Check whether a Buy condition is fulfilled | //| INPUT: price - variable for open price | //| sl - variable for stop loss price, | //| tp - variable for take profit price | //| expiration - variable for expiration time. | //| OUTPUT: true if the condition is fulfilled, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::CheckOpenLong(double& price,double& sl,double& tp,datetime& expiration) { //--- Preparing the data double spread=m_symbol.Ask()-m_symbol.Bid(); double ma =MA(1); double unit =PriceLevelUnit(); //--- Checking the condition if(Open(1)<ma && Close(1)>ma && ma>MA(2)) { price=m_symbol.NormalizePrice(ma-m_limit*unit+spread); sl =m_symbol.NormalizePrice(price-m_stop_loss*unit); tp =m_symbol.NormalizePrice(price+m_take_profit*unit); expiration+=m_expiration*PeriodSeconds(m_period); //--- Condition is fulfilled return(true); } //--- Condition is not fulfilled return(false); } //+------------------------------------------------------------------+ //| Check whether a Sell condition is fulfilled. | //| INPUT: price - variable for open price, | //| sl - variable for stop loss, | //| tp - variable for take profit | //| expiration - variable for expiration time. | //| OUTPUT: true if the condition is fulfilled, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::CheckOpenShort(double& price,double& sl,double& tp,datetime& expiration) { //--- Preparing the data double ma =MA(1); double unit=PriceLevelUnit(); //--- Checking the condition if(Open(1)>ma && Close(1)<ma && ma<MA(2)) { price=m_symbol.NormalizePrice(ma+m_limit*unit); sl =m_symbol.NormalizePrice(price+m_stop_loss*unit); tp =m_symbol.NormalizePrice(price-m_take_profit*unit); expiration+=m_expiration*PeriodSeconds(m_period); //--- Condition is fulfilled return(true); } //--- Condition is not fulfilled return(false); } //+------------------------------------------------------------------+ //| Check whether the condition of modification | //| of a Buy order is fulfilled. | //| INPUT: order - pointer at the object-order, | //| price - a variable for the new open price. | //| OUTPUT: true if the condition is fulfilled, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::CheckTrailingOrderLong(COrderInfo* order,double& price) { //--- Checking the pointer if(order==NULL) return(false); //--- Preparing the data double spread =m_symbol.Ask()-m_symbol.Bid(); double ma =MA(1); double unit =PriceLevelUnit(); double new_price=m_symbol.NormalizePrice(ma-m_limit*unit+spread); //--- Checking the condition if(order.PriceOpen()==new_price) return(false); price=new_price; //--- Condition is fulfilled return(true); } //+------------------------------------------------------------------+ //| Check whether the condition of modification | //| of a Sell order is fulfilled. | //| INPUT: order - pointer at the object-order, | //| price - a variable for the new open price. | //| OUTPUT: true if the condition is fulfilled, otherwise false. | //| REMARK: No. | //+------------------------------------------------------------------+ bool CSampleSignal::CheckTrailingOrderShort(COrderInfo* order,double& price) { //--- Checking the pointer if(order==NULL) return(false); //--- Preparing the data double ma =MA(1); double unit=PriceLevelUnit(); double new_price=m_symbol.NormalizePrice(ma+m_limit*unit); //--- Checking the condition if(order.PriceOpen()==new_price) return(false); price=new_price; //--- Condition is fulfilled return(true); } //+------------------------------------------------------------------+
Und das ist alles. Das Signal kann nun verwendet werden.
Damit der Handelsstrategien-Generator MQL5 Assistent unser Signal auch verwenden kann, sollte der MetaEditor neu gestartet werden (der MQL5 Assistent scannt den Ordner Include\Expert nur beim Booten).
Nach dem Neustart des MetaEditors kann das erzeugte Handelssignale-Modul im MQL5 Assistenten eingesetzt werden:
Abb. 5 Der erzeugte Handelssignale-Generator im MQL5 Assistenten
Die im Abschnitt der Beschreibung der Parameter der Handelssignale festgelegten Eingabeparameter stehen nun zur Verfügung:
Abb. 6 Eingabeparameter des erzeugten Handelssignale-Generators im MQL5 Assistenten
Die besten Werte der Eingabeparameter der implementierten Handelsstrategie erhält man mit Hilfe des Strategie-Testers des MetaTrader 5 Terminals.
Fazit
Der Handelsstrategien-Generator des MQL5 Assistenten erleichtert das Testen von Handelskonzepten ganz enorm. Der Code des generierten Experten beruht auf den Klassen an Handelsstrategien der Standard Library, die zur Erzeugung bestimmter Implementierungen von Handelssignalklassen, Geld- und Risikoverwaltungsklassen und Klassen zur Unterstützung von Positions verwendet werden.
Dieser Beitrag beschreibt, wie Sie Ihre eigene Klasse an Handelssignalen mit der Implementierung von Signalen beim Übertritt des Preises und des gleitenden Durchschnitts schreiben können und wie Sie sie in den Handelsstrategien-Generator des MQL5 Assistenten mit einschließen können. Des Weiteren wird hier auch die Struktur und das Formt der Beschreibung der generierten Klassen für den MQL5 Assistenten erläutert.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/226





- 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.