Elementare Handelssysteme unter Verwendung von Semaphorindikatoren
Nikolay Kositsin | 26 Januar, 2016
Einleitung
Bei Semaphor- oder Signalindikatoren handelt es sich um äußerst einfache Signalgeber für den Eintritt in oder den Rückzug aus dem Markt. Erscheint auf dem aktuellen Balken das Signal für einen Abschluss, wird in dem Diagramm für das betreffende Finanzinstrument ein entsprechendes Zeichen abgebildet, das im Weiteren als Bedingung für den Abschluss eines Geschäfts dienen kann.
Es gibt eine große Menge vergleichbarer Indikatoren, das Wesen des ursprünglichen, auf der Grundlage dieser Indikatoren aufgebauten Handelssystems selbst bleibt jedoch absolut unverändert. Daraus erwuchs die Idee, es in der allereinfachsten und allgemeingültigsten Form umzusetzen. Das ermöglicht uns, das gewonnene Ergebnis zukünftig mit nur geringfügigen Anpassungen mit allen vergleichbaren Indikatoren zu verwenden.
Abb. 1. Der Semaphor-Signalindikator ASCtrend
Abb. 2. Handelssignal für den Abschluss eines Geschäfts unter Verwendung des Semaphor-Signalindikators ASCtrend
Beispiele typischer Semaphor-Signalindikatoren
Derzeit sind in der Codebasis eine Menge derartiger Indikatoren vorhanden. In diesem Beitrag werde ich lediglich einige Hinweise auf deren Ausgangsressourcen geben:
- BykovTrend,
- ASCtrend,
- BrainTrend1Sig,
- BrainTrend2Sig,
- SilverTrend_Signal,
- Stalin,
- WPRSI signal,
- StepMA_NRTR,
- LeManSignal,
- 3Parabolic System,
- PriceChannel_Stop,
- Arrows&Curves,
- Karacatica,
- Sidus.
Neben der Gruppe der Semaphor-Signalindikatoren gibt es auch noch die der Semaphor-Trendindikatoren.
Abb. 3. Der Semaphor-Trendindikator
Abb. 4. Handelssignal für den Abschluss eines Geschäfts unter Verwendung des geglätteten Indikators Heiken_Ashi_Smoothed
In Handelssystemen, die diese Indikatoren nutzen, ändert sich der Programmcode zum Abrufen dieser Signale leicht, der Code des Expert-Systems selbst bleibt jedoch nahezu unverändert.
Beispiele typischer Semaphor-Signalindikatoren
Die Codebasis enthält mehr als genug dieser Indikatoren. In diesem Beitrag werde ich lediglich einige Hinweise auf deren Ausgangsressourcen geben:
- FiboCandles,
- Parabolic SAR,
- X2MA,
- Candles_Smoothed,
- SuperTrend,
- Go,
- 3LineBreak,
- Laguerre,
- Heiken Ashi Smoothed,
- NonLagDot.
Ausgangsdaten zum Aufbau eines Handelssystems
- Semaphorindikator mit den Eingangsparametern, die in dem Expert-System vorhanden sein müssen;
- Aufstellung weiterer Eingangshandelsparameter für das Expert-System:
- der Anteil des für den Abschluss eingesetzten Einlagekapitals;
- die Höhe der Stop Loss- und der Take Profit-Grenze (bei einem Wert von Null sind Pending Orders nicht zu verwenden);
- der Schwund (höchste zulässige Abweichung des veranschlagten Abschlusskurses von dem tatsächlichen);
- die Kennziffer des Balkens, von dem die Handelssignale abgerufen werden;
- die Genehmigung zur Eröffnung „langer“ bzw. „kurzer“ Positionen;
- die Genehmigung zur zwangsweisen Schließung „langer“ bzw. „kurzer“ Positionen aufgrund von Indikatorsignalen.
Es wäre freilich wesentlich einfacher, die Anweisungen zum Abschluss von Geschäften mithilfe universeller Handelsfunktionen zu erteilen. Diese Funktionen sind jedoch recht vielschichtig, weshalb sie in eine eigene Bibliothek verpackt werden müssen, um den zu erstellenden Programmcode so einfach wie möglich zu gestalten.
Der Programmcode eines Expert-Systems, in dem das Semaphor-Handelssystem umgesetzt wird:
//+------------------------------------------------------------------+ //| Exp_ASCtrend.mq5 | //| Copyright © 2011, Nikolay Kositsin | //| Khabarovsk, farria@mail.redcom.ru | //+------------------------------------------------------------------+ #property copyright "Copyright © 2011, Nikolay Kositsin" #property link "farria@mail.redcom.ru" #property version "1.00" //+----------------------------------------------+ //| Expert Advisor indicator input parameters | //+----------------------------------------------+ input double MM=-0.1; // Share of a deposit in a deal, negative values - lot size input int StopLoss_=1000; // Stop loss in points input int TakeProfit_=2000; // Take profit in points input int Deviation_=10; // Max. price deviation in points input bool BuyPosOpen=true; // Permission to buy input bool SellPosOpen=true; // Permission to sell input bool BuyPosClose=true; // Permission to exit long positions input bool SellPosClose=true; // Permission to exit short positions //+----------------------------------------------+ //| ASCtrend indicator input parameters | //+----------------------------------------------+ input ENUM_TIMEFRAMES InpInd_Timeframe=PERIOD_H1; // ASCtrend indicator time frame input int RISK=4; // Risk level input uint SignalBar=1; // Bar index for getting an entry signal //+----------------------------------------------+ int TimeShiftSec; //---- declaration of integer variables for the indicators handles int InpInd_Handle; //---- declaration of integer variables of the start of data calculation int min_rates_total; //+------------------------------------------------------------------+ //| Trading algorithms | //+------------------------------------------------------------------+ #include <TradeAlgorithms.mqh> //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //---- getting ASCtrend indicator handle InpInd_Handle=iCustom(Symbol(),InpInd_Timeframe,"ASCtrend",RISK); if(InpInd_Handle==INVALID_HANDLE) Print(" Failed to get handle of ASCtrend indicator"); //---- initialization of a variable for storing a chart period in seconds TimeShiftSec=PeriodSeconds(InpInd_Timeframe); //---- initialization of variables of the start of data calculation min_rates_total=int(3+RISK*2+SignalBar); //---- return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //---- GlobalVariableDel_(Symbol()); //---- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //---- checking the number of bars to be enough for calculation if(BarsCalculated(InpInd_Handle)<min_rates_total) return; //---- uploading history for IsNewBar() and SeriesInfoInteger() functions normal operation LoadHistory(TimeCurrent()-PeriodSeconds(InpInd_Timeframe)-1,Symbol(),InpInd_Timeframe); //---- declaration of local variables double DnVelue[1],UpVelue[1]; //---- declaration of static variables static bool Recount=true; static bool BUY_Open=false,BUY_Close=false; static bool SELL_Open=false,SELL_Close=false; static datetime UpSignalTime,DnSignalTime; static CIsNewBar NB; //+----------------------------------------------+ //| Searching for deals performing signals | //+----------------------------------------------+ if(!SignalBar || NB.IsNewBar(Symbol(),InpInd_Timeframe) || Recount) // checking for a new bar { //---- zeroing out trading signals BUY_Open=false; SELL_Open=false; BUY_Close=false; SELL_Close=false; Recount=false; //---- copy newly appeared data into the arrays if(CopyBuffer(InpInd_Handle,1,SignalBar,1,UpVelue)<=0) {Recount=true; return;} if(CopyBuffer(InpInd_Handle,0,SignalBar,1,DnVelue)<=0) {Recount=true; return;} //---- getting buy signals if(UpVelue[0] && UpVelue[0]!=EMPTY_VALUE) { if(BuyPosOpen) BUY_Open=true; if(SellPosClose) SELL_Close=true; UpSignalTime=datetime(SeriesInfoInteger(Symbol(),InpInd_Timeframe,SERIES_LASTBAR_DATE))+TimeShiftSec; } //---- getting sell signals if(DnVelue[0] && DnVelue[0]!=EMPTY_VALUE) { if(SellPosOpen) SELL_Open=true; if(BuyPosClose) BUY_Close=true; DnSignalTime=datetime(SeriesInfoInteger(Symbol(),InpInd_Timeframe,SERIES_LASTBAR_DATE))+TimeShiftSec; } //---- searching for the last trading direction for getting positions closing signals //if(!MQL5InfoInteger(MQL5_TESTING) && !MQL5InfoInteger(MQL5_OPTIMIZATION)) //if execution is set to "Random delay" in the Strategy Tester if((BuyPosOpen && BuyPosClose || SellPosOpen && SellPosClose) && (!BUY_Close && !SELL_Close)) { int Bars_=Bars(Symbol(),InpInd_Timeframe); for(int bar=int(SignalBar+1); bar<Bars_; bar++) { if(SellPosClose) { if(CopyBuffer(InpInd_Handle,1,bar,1,UpVelue)<=0) {Recount=true; return;} if(UpVelue[0]!=0 && UpVelue[0]!=EMPTY_VALUE) { SELL_Close=true; break; } } if(BuyPosClose) { if(CopyBuffer(InpInd_Handle,0,bar,1,DnVelue)<=0) {Recount=true; return;} if(DnVelue[0]!=0 && DnVelue[0]!=EMPTY_VALUE) { BUY_Close=true; break; } } } } } //+----------------------------------------------+ //| Performing deals | //+----------------------------------------------+ //---- Closing a long position BuyPositionClose(BUY_Close,Symbol(),Deviation_); //---- Closing a short position SellPositionClose(SELL_Close,Symbol(),Deviation_); //---- Buying BuyPositionOpen(BUY_Open,Symbol(),UpSignalTime,MM,0,Deviation_,StopLoss_,TakeProfit_); //---- Selling SellPositionOpen(SELL_Open,Symbol(),DnSignalTime,MM,0,Deviation_,StopLoss_,TakeProfit_); //---- } //+------------------------------------------------------------------+
Der Code für die Umsetzung einer vergleichbaren Vorstellung ist recht einfach und nachvollziehbar zu erstellen, dennoch seien hier einige Einzelheiten präzisiert.
Der Diagrammzeitraum, in dem der Signalindikator und das Expert-System tätig sind, wird in der Eingabevariablen InpInd_Timeframe des Expert-System festgelegt. Auf diese Weise zieht ein Wechsel des Diagramms, in dem das Expert-System angezeigt wird, keine Änderung an diesem Parameter des Expert-Systems nach sich.
Die zur Ermittlung des Auftauchens eines neuen Balkens erforderliche Funktion IsNewBar() wird als in der Datei TradeAlgorithms.mqh untergebrachte Klasse umgesetzt. Eine derartige Umsetzung ermöglicht die problemlose Verwendung einer beliebigen Anzahl derartiger Funktionen im Code, wobei für jede von ihnen eine eigene Variable des Typs „static CIsNewBar“ festgelegt wird.
Die Variablen UpSignalTime und DnSignalTime dienen zur Speicherung und Weitergabe der Zeit, nach deren Ablauf der nächste Abschluss nach dem jeweils gegenwärtigen erfolgen darf, an die Handelsfunktionen. In der aktuellen Situation erfolgt auf diese Weise das Verbot des Abschlusses mehrerer Geschäfte in einer Richtung in ein und demselben Balken (bei Abschluss eines Geschäftes speichert die Handelsfunktion den Zeitpunkt der Schließung des aktuellen Balkens und führt bis zum Erreichen dieses Zeitpunktes keine weiteren Geschäfte in dieser Richtung ab).
Der Codeblock „Nach der letzten Handelsrichtung suchen, um Signale für das Schließen einer Position zu empfangen“ in der Funktion OnTick() ist zur Erzeugung von Signalen für die Schließung von Positionen auf Balken ohne Handelssignale erforderlich. Beim üblichen Einsatz eines Expert-Systems werden diese nicht benötigt, aber bei einer Unterbrechung der Internetverbindung ist es gut möglich, dass ein neu entstehendes Handelssignal verpasst wird. Eine Handelsposition rückwirkend zu eröffnen, dürfte kaum zweckmäßig sein, deshalb sollte man sie wenigstens schließen.
Verwendung eines Handelssystems mit anderen Semaphor-Signalindikatoren
Jetzt sind, sofern sich die Notwendigkeit zur Verwendung die Codes mit einem anderen Semaphor-Signalindikator ergeben sollte, folgende Arbeitsschritte auszuführen:
- die Eingabe aller erforderlichen Parameter des neuen Indikators in die Eingangsparameter des Expert-Systems anstelle derjenigen seines Vorgängers;
- die Änderung des Codes für den Abruf des Indikator-Handles in dem Block OnInit();
- die Festlegung der Kennziffern für die Indikatorpuffer zur Speicherung der Handelssignale für den Kauf und den Verkauf sowie ihre entsprechende Eingabe im Block OnTick() in die Aufrufe für die Funktion CopyBuffer(). In diesem Fall kommen bei uns die Puffer Null und Eins des Indikators zum Einsatz;
- die Änderung der Bereitstellung der Variablen für den Beginn der Datenberechnung (min_rates_total) in dem Expert-System in Übereinstimmung mit dem Programmcode des Indikators;
- die Änderung des Blocks „Nach der letzten Handelsrichtung suchen, um Signale für das Schließen einer Position zu empfangen“ in der Funktion OnTick() in Übereinstimmung mit dem Programmcode des Indikators.
Verwendung eines Handelssystems mit anderen Semaphor-Trendindikatoren
Zur Verwendung dieses Handelssystems mit einem Semaphor-Trendindikator wurde der Code des Expert-Systems im Block zur Ermittlung der Signale für Vorgänge der Funktion OnTick() ein wenig modifiziert. Bei einem Expert-System auf der Grundlage des Indikators FiboCandles sieht die Code folgendermaßen aus:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //---- checking the number of bars to be enough for calculation if(BarsCalculated(InpInd_Handle)<min_rates_total) return; //---- uploading history for IsNewBar() and SeriesInfoInteger() functions LoadHistory(TimeCurrent()-PeriodSeconds(InpInd_Timeframe)-1,Symbol(),InpInd_Timeframe); //---- declaration of local variables double TrendVelue[2]; //---- declaration of static variables static bool Recount=true; static bool BUY_Open=false,BUY_Close=false; static bool SELL_Open=false,SELL_Close=false; static datetime UpSignalTime,DnSignalTime; static CIsNewBar NB; //+----------------------------------------------+ //| Searching for deals performing signals | //+----------------------------------------------+ if(!SignalBar || NB.IsNewBar(Symbol(),InpInd_Timeframe) || Recount) // checking for a new bar { //---- zeroing out trading signals BUY_Open=false; SELL_Open=false; BUY_Close=false; SELL_Close=false; Recount=false; //---- copy the newly obtained data into the arrays if(CopyBuffer(InpInd_Handle,4,SignalBar,2,TrendVelue)<=0) {Recount=true; return;} //---- getting buy signals if(TrendVelue[0]==1 && TrendVelue[1]==0) { if(BuyPosOpen) BUY_Open=true; if(SellPosClose)SELL_Close=true; UpSignalTime=datetime(SeriesInfoInteger(Symbol(),InpInd_Timeframe,SERIES_LASTBAR_DATE))+TimeShiftSec; } //---- getting sell signals if(TrendVelue[0]==0 && TrendVelue[1]==1) { if(SellPosOpen) SELL_Open=true; if(BuyPosClose) BUY_Close=true; DnSignalTime=datetime(SeriesInfoInteger(Symbol(),InpInd_Timeframe,SERIES_LASTBAR_DATE))+TimeShiftSec; } //---- searching for the last trading direction for getting positions closing signals //if(!MQL5InfoInteger(MQL5_TESTING) && !MQL5InfoInteger(MQL5_OPTIMIZATION)) //if execution is set to "Random delay" in the Strategy Tester { if(SellPosOpen && SellPosClose && TrendVelue[1]==0) SELL_Close=true; if(BuyPosOpen && BuyPosClose && TrendVelue[1]==1) BUY_Close=true; } } //+----------------------------------------------+ //| Performing deals | //+----------------------------------------------+ //---- Closing a long position BuyPositionClose(BUY_Close,Symbol(),Deviation_); //---- Closing a short position SellPositionClose(SELL_Close,Symbol(),Deviation_); //---- Buying BuyPositionOpen(BUY_Open,Symbol(),UpSignalTime,MM,0,Deviation_,StopLoss_,TakeProfit_); //---- Selling SellPositionOpen(SELL_Open,Symbol(),DnSignalTime,MM,0,Deviation_,StopLoss_,TakeProfit_); //---- }
In diesem Fall stammen die Handelssignale aus lediglich einem Indikatorpuffer, nämlich dem mit den Farben (er beinhaltet die Kennziffern für die Farben), dessen Daten nur zwei Werte annehmen können: „0“ bei einem wachsenden und „1“ bei einem schrumpfenden Markt. Der Code im Block „Nach der letzten Handelsrichtung suchen, um Signale für das Schließen einer Position zu empfangen“ wurde in diesem Beispiel äußerst einfach gehalten, um die Trendrichtung auf jedem Balken möglichst unmittelbar aus der entsprechenden Zelle des Indikatorpuffers abfragen zu können.
In dem Block „Abschluss von Geschäften“ kommen zunächst die Funktionen zum Schließen von Positionen und danach sofort die zum Eröffnen. Wäre es andersherum, so würden sich bei einer Prüfung im Modus „Nur Eröffnungskurse“ die Abschlüsse auf einem Balken nur schließen lassen, aber sie könnten nicht gleichzeitig eröffnet werden. Folglich würden wir ein ernstlich gestörtes Bild des Handels erhalten.
Prüfung des Handelssystems
Bevor wir zu der Prüfung des Handelssystems kommen, müssen wir ein nicht ganz unwesentliches Detail präzisieren. Dabei geht es darum, dass das Expert-System, wenn die Eingabevariable SignalBar den Wert Null hat, auf dem jeweils aktuellen Balken Signale für den Abschluss von Geschäften erhalten wird. Das von dem aktuellen Balken ausgesandte Signal ist jedoch keine belastbare Bedingung für einen Austausch des auf dem vorherigen Balken aktiven Trends gegen eben dieses. Die Signale auf dem aktuellen Balken können erscheinen und verschwinden, aber der Trend auf diese verschwundenen Signale zu kann auch nach deren Verschwinden noch recht lange anhalten. Davon kann man sich leicht überzeugen, wenn man das Expert-System bei jeder Kursänderung mit eingeschalteter grafischer Darstellung und der Variablen SignalBar mit dem Wert Null prüft. Die grafische Darstellung der Tätigkeit des Indikators ASCtrend ist in diesem Fall ein mehr als anschaulicher Beleg für diesen Umstand.
Auch hier ist lediglich der Modus „Jede Kursänderung“ (Every Tick) zur Optimierung des Expert-Systems durch den Empfang des Signals von dem aktuellen Balken geeignet, während bei Signalen von anderen, bereits geschlossenen Balken sogar der Modus „Nur Eröffnungskurse“ vollkommen ausreicht. Dadurch werden die Verhaltensanalysevorgänge des Handelssystems ohne ernsthafte Einbußen bei der Analysequalität um ein Vielfaches beschleunigt.
Folglich ist es ratsam, sich der Prüfung und Optimierung derartiger Handelssysteme unter Verwendung der Signale von dem aktuellen Balken zu enthalten!
Also prüfen wir unser Expert-System mit den Standardparametern für das Währungspaar EUR/USD in dem Zeitraum von Jahresbeginn bis Anfang Dezember:
Abb. 5. Prüfergebnis des Expert-Systems Exp_ASCtrend mit Standardparametern auf EUR/USD H1
Nach einer geringfügigen Änderung der Einstellungen des Expert-Systems in dem Prüfprogramm Strategy Tester ist es nicht schwer, die erfolgreichste Parameterkombination für das Expert-System angesichts der vorhandenen Verlaufsdaten auszuwählen.
Abb. 6. Prüfergebnis des Expert-Systems Exp_ASCtrend nach der Optimierung mithilfe verbesserter Parameter auf EUR/USD H1
Der Vorgang zur Verbesserung dieses Handelssystems weist keinerlei Besonderheiten auf, deshalb verweise ich zu seinen Einzelheiten lediglich auf den diesen gewidmeten Beitrag: „MQL5: Leitfaden zum Testen und Optimieren von Expert Advisors in MQL5“.
Selbstverständlich wäre es naiv, von einem derartigen elementaren Handelssystem herausragende Ergebnisse zu erwarten. Dennoch ist es absolut möglich, einen hinlänglich zufriedenstellenden Handel zu erreichen, wenn man ein vergleichbares halbautomatisches System sachkundig einsetzt und es regelmäßig auf das aktuelle Marktgeschehen abstimmt.
Beispielsweise zeigt das Diagramm EUR/USD H12 für 2011 von Januar bis Mai einen aufsteigenden Trend, dessen Ermittlung in der Frühphase seines Entstehens keiner besonderen Anstrengung bedurfte:
Abb. 7. Diagramm EUR/USD H12 (Januar/Mai 2011)
Es wäre interessant, das Expert-System in diesem Zeitraum mit den Standardparametern zu prüfen und dabei die Möglichkeit einzuschließen, nur Käufe zu tätigen, und dabei nur 5 % der Einlage (MM = 0,05) einzusetzen. Hier haben wir das Ergebnis der Prüfung des Expert-Systems mit diesen Parametern in einem Stundendiagramm (H1):
Abb. 8. Prüfergebnis des Expert-Systems Exp_ASCtrend mit Standardparametern auf EUR/USD H1 für Januar/Mai 2011 (nur „long“ Positionen, MM = 0,05)
Natürlich liegt die gesamte Verantwortung für die Auswahl der Handelsrichtung bei einem solchen Herangehen auf den Schultern des Händlers. Wenn wir jedoch bedenken, dass dies besser mithilfe von Diagrammen für größere Zeiträume geschieht, dürften sich dabei eigentlich keine besonderen Schwierigkeiten ergeben.
Umgestaltung des Handelsmoduls zur Verwendung mit einem anderen Indikator
Damit könnte dieser Punkt auch schon abgehakt sein, bestünde nicht in dem Bearbeitungsprogramm MetaEditor die Möglichkeit zur Erstellung von Expert-Systemen auf der Grundlage vorgefertigter Handelsmodule. Allein die Erstellung dieser Module lediglich anhand des von mir oben vorgestellten Materials ist recht umfangreich und erfordert eine gesonderte Betrachtung. Deswegen werde ich in diesem Beitrag bei den bereits beschriebenen Handelsmodulen bleiben, die den von mir vorgeschlagenen Handelssystemen vollkommen entsprechen. Und erst im Anschluss daran werde ich mich mit den Einzelheiten der Umgestaltung dieser Module entsprechend den einzelnen Signalindikatoren befassen, wobei ich von der überflüssigen Vertiefung der Betrachtung dieses Vorgangs absehen werde.
Wir setzen voraus, dass bereits eine Sammlung der Handelsmodule für Semaphor-Signalsysteme (MySignals.zip) vorhanden ist, und lediglich ein vergleichbares Modul für irgendeinen beliebigen Indikator angelegt werden soll. Nehmen wir den Indikator BykovTrendSignal.mq5, einen typischen Semaphor-Signalindikator. Zunächst einmal müssten wir in dieser Sammlung (Indicators.zip) den mit diesem Indikator am ehesten vergleichbaren Indikator ausfindig machen. Mithilfe der grafischen Darstellung ermitteln wir, dass es sich bei dem in diesem Beitrag als erstem behandelten Indikator (ASCtrend) um denjenigen handelt, der ihm am ähnlichsten ist. Für die Umgestaltung verwenden wir deshalb das Handelsmodul eben dieses Indikators.
Der zu verwendende Indikator selbst erscheint im Programmcode unter der Bezeichnung „BykovTrend“ und weist folgende Eingangsparameter auf:
//+----------------------------------------------+ //| Indicator input parameters | //+----------------------------------------------+ input int RISK=3; input int SSP=9; //+----------------------------------------------+
Und wir benötigen die Kennziffern der Indikatorpuffer mit den Signalen für den Abschluss von Geschäften. In unserem Fall sind das: die „0“ für Verkaufssignale und die „1“ bei Kaufsignalen.
Jetzt, da wir wissen, welches Modul wir für die Umgestaltung verwenden müssen, kopieren wir dieses Modul unter dem Dateinamen BykovTrendSignal.mqh in den Ordner \MQL5\Include\Expert\Signal\MySignals\ und öffnen es anschließend in dem Bearbeitungsprogramm MetaEditor. In dem verwendeten Code taucht immer wieder der Ausdruck „ASCtrend“ (die Bezeichnung des vorhergehenden Indikators) auf. Er muss durch die Bezeichnung für den neuen Indikator, „BykovTrend“, ersetzt werden. Dazu drücken wir gleichzeitig die Tasten „Strg“ (Ctrl) und „H“ und nehmen den erforderlichen Austausch vor:
Abb. 9. Austausch der Indikatorbezeichnung im Code des Handelsmoduls
Jetzt kommen wir zum mühsamsten Teil der Arbeit. Wir müssen im Code des Handelsmoduls alles austauschen, was die Eingangsparameter betrifft. Das Vorgehen ähnelt weitgehend dem in dem Artikel „Der MQL5-Assistent: Erstellung eines Modules mit Handelssignalen“ dargestellten.
Zunächst müssten einige Änderungen in dem Kommentarblock zur Beschreibung der Klasse der Handelssignale für den MQL5-Assistenten vorgenommen werden:
//+----------------------------------------------------------------------+ //| Description of the class | //| Title=The signals based on BykovTrend indicator | //| Type=SignalAdvanced | //| Name=BykovTrend | //| Class=CBykovTrendSignal | //| Page= | //| Parameter=BuyPosOpen,bool,true,Permission to buy | //| Parameter=SellPosOpen,bool,true,Permission to sell | //| Parameter=BuyPosClose,bool,true,Permission to exit a long position | //| Parameter=SellPosClose,bool,true,Permission to exit a short position | //| Parameter=Ind_Timeframe,ENUM_TIMEFRAMES,PERIOD_H1,Timeframe | //| Parameter=RISK,int,4,Risk level | //| Parameter=SSP,int,9,SSP | //| Parameter=SignalBar,uint,1,Bar index for entry signal | //+----------------------------------------------------------------------+ //--- wizard description end //+----------------------------------------------------------------------+ //| CBykovTrendSignal class. | //| Purpose: Class of generator of trade signals based on | //| BykovTrend indicator https://www.mql5.com/ru/code/497/. | //| Is derived from the CExpertSignal class. | //+----------------------------------------------------------------------+
Beide Indikatoren enthalten ein und dieselbe Eingangsvariable RISK, die deshalb an Ort und Stelle bleiben kann, ihr voreingestellter Wert ist in diesen Indikatoren jedoch jeweils ein anderer. Grundsätzlich ist dieser Unterschied bei den Werten der RISK-Variablen nicht ausschlaggebend und kann bestehen bleiben. Es wurde ein Kommentarzeile bezüglich der Variablen SSP hinzugefügt:
//| Parameter=SSP,int,9,SSP |
Und in der Codebasis wurde der Verweis auf den Indikator geändert:
//| Purpose: Class of generator of trade signals based on | //| BykovTrend values https://www.mql5.com/ru/code/497/. |
Jetzt muss alles, was die Änderungen der Eingangsvariablen betrifft, auch in der Beschreibung der Klasse der Handelssignale CBykovTrendSignal selbst wiedergegeben werden. Bei uns erscheint eine Zeile mit der Deklaration einer neuen globalen Variablen der Klasse m_SSP mit folgenden Einstellungsparametern:
uint m_SSP; // SSP
sowie eine Zeile mit der Deklaration einer neuen Methode zur Festlegung der Einstellungsparameter für SSP():
void SSP(uint value) { m_SSP=value; }
Alles, was die Eingangsvariable RISK in dem von uns angelegten Handelssignalmodul betrifft, ist identisch mit dem ursprünglichen Modul, weshalb in diesem sowie in den übrigen Codeblöcken des Handelsmoduls keine Änderungen erforderlich sind.
Kommen wir jetzt zum Konstruktor der Klasse CBykovTrendSignal::CBykovTrendSignal(). In diesem Block müssen wir die Bereitstellung der neuen Variablen ergänzen:
m_SSP=4;
In dem Block CBykovTrendSignal::ValidationSettings() zur Prüfung der Einstellungsparameter ist die neue Variable auf ihre Richtigkeit zu überprüfen:
if(m_SSP<=0) { printf(__FUNCTION__+": SSP must be above zero"); return(false); }
Anschließend können wir uns dem Block zur Bereitstellung des Indikators BykovTrend zuwenden: BykovTrendSignal::InitBykovTrend(). Der neue Indikator verfügt über eine abweichende Anzahl von Eingangsvariablen, weshalb die Dimension des deklarierten Datenfeldes für die Eingangsparameter ebenfalls eine andere ist:
//--- setting the indicator parameters MqlParam parameters[3];
In dem hier vorliegenden Fall benötigen wir eine Dimension für die Speicherbezeichnung, den „string name“, des Indikators sowie zwei weitere für seine Eingangsparameter.
Nun müssen wir unter Angabe der Art der darin zu speichernden Variablen eine neue Zelle des Datenfeldes für die Eingabeparameter bereitstellen:
parameters[2].type=TYPE_INT; parameters[2].integer_value=m_SSP;
Anschließend ändern wir in diesem Block die Anzahl der Eingangsparameter im Aufruf zur Bereitstellung des Indikators auf 3:
//--- object initialization if(!m_indicator.Create(m_symbol.Name(),m_Ind_Timeframe,IND_CUSTOM,3,parameters))
Die Anzahl der Anzeigepuffer im Indikator bleibt unverändert bei zwei, weshalb in unserem Fall an der Zeile zur Bereitstellung der Anzahl der Indikatorpuffer keinerlei Änderungsbedarf besteht:
//--- number of buffers if(!m_indicator.NumBuffers(2)) return(false);
Die Indikatoren ASCtrend und BykovTrend verfügen jeweils über zwei Indikatorpuffer, wobei die Zweckbestimmung der Puffer absolut identisch ist. Der Puffer „Null“ dient zur Zwischenspeicherung der Verkaufssignale, während der Puffer mit der Kennziffer „1“ die Kaufsignale aufnimmt. Folglich muss in den Codeblöcken der Funktionen CBykovTrendSignal::LongCondition() und CBykovTrendSignal::ShortCondition() zur Ausgabe von Handelssignalen nichts geändert werden, und wir können die Umgestaltung des Moduls mit den Handelsmodulen als abgeschlossen betrachten.
Generell unterscheiden sich alle Semaphorindikatoren, weshalb diese Blöcke bei unterschiedlichen Indikatoren erheblich voneinander abweichen können. Das Handelsmodulearchiv MySignals.zip sowie das entsprechende Indikatorarchiv Indicators.zip beinhalten eine ausreichende Menge an Beispielen für unterschiedliche Verfahren zum Anlegen von Indikatoren. Wenn man sich mit ihnen ein wenig vertraut gemacht hat, findet man sich mühelos in den Einzelheiten dieses Austauschvorgangs und den möglichen Codevarianten zu diesem Zweck zurecht.
Jetzt möchte ich unsere Aufmerksamkeit auf die Eingangsvariable Ind_Timeframe des Handelssignalmoduls konzentrieren. Mithilfe dieser Variablen wird der zeitliche Rahmen, innerhalb dessen er tätig ist, in den Indikator geladen. Das erstellte Expert-System selbst arbeitet jedoch in dem Zeitraum, dem es zugewiesen wurde. Damit das Modul ordnungsgemäß funktioniert, darf der Zeitraum der Eingangsvariablen Ind_Timeframe also niemals größer sein als der Zeitraum des Diagramms, in dem das Expert-System ausgeführt wird.
Abschließend möchte ich noch auf eine Besonderheit bei der Programmierung von Handelssignalmodulen hinweisen. Mitunter enthält der Code des ursprünglichen Indikators als Varianten für die Eingangsvariablen des Moduls benutzerdefinierte Aufzählungen. So erweist zum Beispiel bei dem Indikator Candles_Smoothed die benutzerdefinierte Aufzählung Smooth_Method als Spielart der Variablen MA_SMethod:
//+-----------------------------------+ //| Declaration of enumerations | //+-----------------------------------+ enum Smooth_Method { MODE_SMA_, // SMA MODE_EMA_, // EMA MODE_SMMA_, // SMMA MODE_LWMA_, // LWMA MODE_JJMA, // JJMA MODE_JurX, // JurX MODE_ParMA, // ParMA MODE_T3, // T3 MODE_VIDYA, // VIDYA MODE_AMA, // AMA }; */ //+----------------------------------------------+ //| Indicator input parameters | //+----------------------------------------------+ input Smooth_Method MA_SMethod=MODE_LWMA; // Smoothing method input int MA_Length=30; // Smoothing depth input int MA_Phase=100; // Smoothing parameter // for JJMA varying within the range -100 ... +100, // for VIDIA it is a CMO period, for AMA it is a slow average period //+----------------------------------------------+
In einer solchen Situation müssen diese Eingangsvariablen und alles im Handelssignalmodul (Candles_SmoothedSignal.mqh) mit ihnen Verbundene in Variablen der Art „int“ bzw. „uint“ umgewandelt werden. Und zur Vereinfachung der Handhabung dieser Eingangsvariablen im Rahmen des Codes des bereits erstellten Expert-Systems müssen die Deklaration der benutzerdefinierten Aufzählungen bis zu den Eingangsparametern des Expert-Systems rückgängig gemacht und die benötigten Eingangsvariablen (des Expert-Systems ExpM_Candles_Smoothed) ausgetauscht werden:
//+------------------------------------------------------------------+ //| Declaration of enumerations | //+------------------------------------------------------------------+ enum Smooth_Method { MODE_SMA_, // SMA MODE_EMA_, // EMA MODE_SMMA_, // SMMA MODE_LWMA_, // LWMA MODE_JJMA, // JJMA MODE_JurX, // JurX MODE_ParMA, // ParMA MODE_T3, // T3 MODE_VIDYA, // VIDYA MODE_AMA, // AMA }; //+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ //--- inputs for expert input string Expert_Title ="Candles_Smoothed"; // Document name ulong Expert_MagicNumber =29976; // bool Expert_EveryTick =false; // //--- inputs for main signal input int Signal_ThresholdOpen =40; // Signal threshold value to open [0...100] input int Signal_ThresholdClose=20; // Signal threshold value to close [0...100] input double Signal_PriceLevel =0.0; // Price level to execute a deal input double Signal_StopLevel =50.0; // Stop Loss level (in points) input double Signal_TakeLevel =50.0; // Take Profit level (in points) input int Signal_Expiration =1; // Expiration of pending orders (in bars) input bool Signal__BuyPosOpen =true; // Candles_Smoothed() Permission to buy input bool Signal__SellPosOpen =true; // Candles_Smoothed() Permission to sell input bool Signal__BuyPosClose =true; // Candles_Smoothed() Permission to exit a long position input bool Signal__SellPosClose =true; // Candles_Smoothed() Permission to exit a short position input ENUM_TIMEFRAMES Signal__Ind_Timeframe=PERIOD_H1; // Candles_Smoothed() Timeframe input Smooth_Method Signal__MA_SMethod =4; // Candles_Smoothed() Smoothing method (1 - 10) input uint Signal__MA_Length =30; // Candles_Smoothed() Smoothing depth input uint Signal__MA_Phase =100; // Candles_Smoothed() Smoothing parameter input uint Signal__SignalBar =1; // Candles_Smoothed() Bar index for the entry signal input double Signal__Weight =1.0; // Candles_Smoothed() Weight [0...1.0] //--- inputs for money input double Money_FixLot_Percent =10.0; // Percent input double Money_FixLot_Lots =0.1; // Fixed volume
Im vorliegenden Fall ist das mithilfe der Eingangsvariablen Signal__MA_SMethod geschehen.
Das Verstehen des Vorgehens bei der Umgestaltung des Codes kann erheblich beschleunigt werden, wenn man in dem Bearbeitungsprogramm beide Codevarianten (ASCtrendSignal.mqh und BykovTrendSignal.mqh) gleichzeitig öffnet (die eine links, die andere rechts), und den Code in beiden aufmerksam vergleicht.
Fazit
In dem, diesem Beitrag als Anlage beigefügten Archiv Experts.zip habe ich eine mehr als ausreichende Anzahl von Expert-Systemen auf der Grundlage von Semaphor-Handelssystemen zusammengestellt, damit auch Neulinge auf dem Gebiet der Programmierung von Expert-Systemen sich mit den Feinheiten der Programmierung vergleichbarer Codes zurechtfinden oder wenigstens grundlegend mit fertigen Expert-Systemen auf der Grundlage einigermaßen geläufiger Indikatoren arbeiten können.
Für alle, die sich als Grundlage für die Erstellung eigener Handelssysteme für die Möglichkeiten eines Handelsstrategiegenerators entschieden haben, werden alle hier vorgeschlagenen Expert-Systeme zusätzlich als Handelsmodule präsentiert. Diese Module befinden sich in dem Archiv MySignals.zip, die auf ihrer Grundlage erstellten Handelssysteme dagegen im Archiv Expertsez.zip. Die in den Expert-Systemen verwendeten Indikatoren befinden sich in dem Archiv Indicators.zip. Die Pfade zum Entpacken der Dateien sind:
- Experts.zip: „\MQL5\Experts\“;
- Expertsez.zip: „\MQL5\Experts\“;
- MySignals.zip: „\MQL5\Include\Expert\Signal\MySignals\“;
- Indicators.zip: „\MQL5\Indicators“;
- SmoothAlgorithms.mqh: „\Include\“;
- TradeAlgorithms.mqh: „\Include\“.
Zum gleichzeitigen Erstellen aller Dateien müssen wir das Bearbeitungsprogramm MetaEditor nach dem Entpacken neu starten, das Symbol MQL5 mit der rechten Maustaste anklicken und anschließend in dem angezeigten Kontextmenü den Befehl „Erstellen“ (Compile) auswählen.
Die Datei SmoothAlgorithms.mqh ist zum Erstellen einiger Indikatoren aus dem Archiv Indicators.zip erforderlich, während die Datei TradeAlgorithms.mqh zur Erstellung aller Expert-Systeme aus dem Archiv Experts.zip benötigt wird.