![Anlegen eines systemübergreifenden mehrwährungsfähigen automatischen Handelssystems](https://c.mql5.com/i/articles/overlay.png)
Anlegen eines systemübergreifenden mehrwährungsfähigen automatischen Handelssystems
Einleitung
Ich denke, es werden sich nicht wenige Devisenhändler finden, die mit mehr als nur einem Währungspaar (Kürzel) handeln und mehrere Strategien verfolgen. Eine solche Herangehensweise ermöglicht nicht nur eine mögliche Steigerung der Gewinne, sondern durch eine durchdachte Kapitalverwaltung auch eine beträchtliche Verringerung des Risikos größerer Einbußen. Beim Aufbau eines jeden Expert-Systems besteht der erste Schritt bei der Überprüfung der Effektivität der in dem Programm angelegten Strategie in dessen Optimierung mit dem Ziel der Ermittlung der optimalen Eingangsparameter.
Sobald die Parameterwerte ermittelt sind, kann das Expert-System für den Handel eingerichtet werden, wobei allerdings eine nicht ganz unwichtige Frage noch einer Antwort harrt: Wie würden die Prüfergebnisse aussehen, wenn der Händler all seine Strategien in einem einzigen Expert-System bündeln könnte? Manchmal kommt das „böse Erwachen“, wenn man feststellen muss, dass die Rückgänge bei einigen Kürzeln oder Strategien sich irgendwann derart aufsummieren, dass sie zu einem entsetzlichen Gesamtverlust bis hin zu einer Nachschussforderung führen können.
In dem hier vorliegenden Beitrag wird ein Modell zur Erstellung eines systemübergreifenden mehrwährungsfähigen Expert-Systems vorgestellt, dass dabei helfen soll, eine Antwort auf diese entscheidende Frage zu finden.
1. Das Gerüst des Expert-Systems
Grob gezeichnet sieht das Gerüst etwa so aus:
Abb. 1. Gerüst eines systemübergreifenden mehrwährungsfähigen automatischen Handelssystems
Wie zu sehen ist, bildet der Arbeitsgang for die Grundlage für das Programm. Jede Strategie wird in einem Arbeitsuntergang (Zyklus) untergebracht, in dem jede Iteration für den Handel mit jedem Kürzel gesondert zuständig ist. So kann eine unbegrenzte Zahl Strategien aufgenommen werden. Hauptsache unser Rechner verfügt über die entsprechenden Kapazitäten, um ein solches Programm „zu schlucken“.
Es sei daran erinnert, dass in MetaTrader 5 zu jedem gehandelten Kürzel nur eine Position vorhanden sein darf. Diese ist die Summe der Posten aller zuvor abgeschlossenen Käufe und Verkäufe. Deshalb wird das Ergebnis der gleichzeitigen Prüfung mehrerer Strategien an einem Kürzel nicht mit der Summe der einzelnen Prüfungen jeder dieser Strategien an demselben Kürzel übereinstimmen.
Zur eingehenderen Untersuchung des Aufbaus eines entsprechenden Expert-Systems verwenden wir zwei Strategien für den Handel mit jeweils zwei Kürzeln:
Strategie A:
- Kaufen (Buy): der Nachfragekurs „Ask“ berührt den unteren Rand des anhand des niedrigen (low) Kurses berechneten Indikators „Bollinger Bänder“.
Schließen (Close): der Gebotskurs „Bid“ berührt den unteren Rand des anhand des hohen (high) Kurses berechneten Indikators „Bollinger Bänder“. - Verkaufen (Sell): der Gebotskurs „Bid“ berührt den oberen Rand des anhand des hohen (high) Kurses berechneten Indikators „Bollinger Bänder“.
Schließen (Close): der Nachfragekurs „Ask“ berührt den oberen Rand des anhand des niedrigen (low) Kurses berechneten Indikators „Bollinger Bänder“. - Beschränkung: Nur ein Abschluss je Balken.
Strategie В:
- Kaufen (Buy): der vorhergehende Balken weist eine fallende Tendenz auf (Schlusskurs < Eröffnungskurs) und der Nachfragepreis (Ask) berührt dessen Höchstwert (Maximum).
Schließen (Close): bei den Grenzen Stop Loss oder Take Profit. - Verkaufen (Buy): der vorhergehende Balken weist eine steigende Tendenz auf (Schlusskurs > Eröffnungskurs) und der Gebotspreis (Bid) berührt dessen Tiefstwert (Minimum).
Schließen (Close): bei den Grenzen Stop Loss oder Take Profit. - Beschränkung: Nur ein Abschluss je Balken.
Um von dem Auftreten neuer Kursbewegungen (Ticks) bei dem Kürzel, für das das Expert-System geprüft oder betrieben wird, unabhängig zu sein, empfiehlt es sich, für den Handel mit mehreren Währungen die Funktion OnTimer() zu verwenden.
Dazu geben wir bei der Bereitstellung des Expert-Systems mithilfe der Funktion EventSetTimer() an, mit welcher Frequenz das Ereignis zum Aufruf der Berechnung des Programms erzeugt werden soll, und weisen das Ausgabegerät (Terminal) bei seiner Bereinigung mit der Funktion EventKillTimer() an, die Erzeugung von Ereignissen zu beenden:
// Include standard libraries // Create external parameters // Create arrays, variables, indicator handles, etc. //--- Initialization of the Expert Advisor int OnInit() { //--- Set event generation frequency EventSetTimer(1); // 1 second // ... return(0); } void OnTimer() { // ... } //--- Deinitialization of the Expert Advisor void OnDeinit(const int reason) { //--- Stop event generation EventKillTimer(); // ... }
Statt EventSetTimer() kann auch die Funktion EventSetMillisecondTimer() verwendet werden. In ihr kann die Frequenz millisekundengenau eingestellt werden, allerdings sollte sie nicht zu übermäßig häufigen Aufrufen zur Berechnung des Programms missbraucht werden.
Um auf die Eigenschaften des Kontos, der Position und des Kürzels sowie auf die Funktionen für den Handel zugreifen zu können, verwenden wir die Klassen CAccountInfo, CPositionInfo, CSymbolInfo bzw. CTrade. Wir verknüpfen sie mit unserem Expert-System:
//--- Include standard libraries #include <Trade\AccountInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\Trade.mqh>
Da das Expert-System auf Arbeitsgängen der Art „for“ beruht, müssen die externen Parameter in Datenfeldern abgelegt werden. Zunächst legen wir eine der Anzahl der Kürzel für jede Strategie entsprechende Anzahl von Konstanten an.
//--- Number of traded symbols for each strategy #define Strategy_A 2 #define Strategy_B 2
Danach erstellen wir die externen Parameter. Anhand der Konstanten bestimmen wir die Dimension der Datenfelder, in die sie kopiert werden. Außerdem legen wir die Bezeichner für die Indikatoren und weitere globale Variablen an.
Es folgt ein Beispiel für die Strategie A mit nur einem Kürzel:
//------------------- External parameters of strategy A input string Data_for_Strategy_A="Strategy A -----------------------"; //--- Symbol 0 input string Symbol_A0 = "EURUSD"; // Symbol input bool IsTrade_A0 = true; // Permission for trading //--- Bollinger Bands (BB) parameters input ENUM_TIMEFRAMES Period_A0 = PERIOD_H1; // ВВ period input uint BBPeriod_A0 = 20; // Period for calculation of the moving average of BB input int BBShift_A0 = 0; // Horizontal shift of ВВ input double BBDeviation_A0 = 2.0; // Number of standard deviations of BB //... //--- General parameters of strategy A input double DealOfFreeMargin_A = 1.0; // Percent of free margin for a deal input uint MagicNumber_A = 555; // Magic number input uint Slippage_A = 100; // Permissible slippage for a deal //... //------------- Set variables of strategy A ----- //--- Arrays for external parameters string Symbol_A[Strategy_A]; bool IsTrade_A[Strategy_A]; ENUM_TIMEFRAMES Period_A[Strategy_A]; int BBPeriod_A[Strategy_A]; int BBShift_A[Strategy_A]; double BBDeviation_A[Strategy_A]; //--- Arrays for global variables double MinLot_A[Strategy_A],MaxLot_A[Strategy_A]; double Point_A[Strategy_A],ContractSize_A[Strategy_A]; uint DealNumber_A[Strategy_A]; datetime Locked_bar_time_A[Strategy_A],time_arr_A[]; //--- Indicator handles int BB_handle_high_A[Strategy_A]; int BB_handle_low_A[Strategy_A]; //--- Arrays for indicator values double BB_upper_band_high[],BB_lower_band_high[]; double BB_upper_band_low[],BB_lower_band_low[]; //--- Class CTrade Trade_A; //... //--- Set global variables for all strategies long Leverage; //--- Classes CAccountInfo AccountInfo; CPositionInfo PositionInfo; CSymbolInfo SymbolInfo;
Um die Möglichkeit zu haben, den Handel mit einem bestimmten Kürzel auszuschließen, wird die logische Variable IsTrade_A0 angelegt und ganz am Anfang des Arbeitsgangs for platziert.
2. Die Bereitstellung des Expert-Systems
Zunächst beziehen wir die für alle Strategien benötigten Werte, zum Beispiel den Umfang der Hebelwirkung. Da die Hebelwirkung für das Handelskonto unabhängig von der jeweiligen Strategie oder des Kürzels gilt, besteht keine Notwendigkeit, ihren Wert in die Datenfelder zu kopieren:
//--- Get the leverage for the account
Leverage=AccountInfo.Leverage();
Wir kopieren die externen Variablen in die Datenfelder.
//--- Copy external variables to arrays Symbol_A[0] =Symbol_A0; IsTrade_A[0] =IsTrade_A0; Period_A[0] =Period_A0; BBPeriod_A[0] =(int)BBPeriod_A0; BBShift_A[0] =BBShift_A0; BBDeviation_A[0]=BBDeviation_A0;
Wenn ein externer Parameter in einer Art vorgegeben ist, die in eine andere umgewandelt werden muss, sollte dies praktischerweise beim Kopieren in die Datenfelder geschehen.
In diesem Fall sehen wir, dass BBPeriod_A0 als uint angelegt wurde, um den Anwender daran zu hindern, einen negativen Wert anzugeben. Wir wandeln ihn an dieser Stelle in die Art int um und kopieren ihn in das ebenfalls als int angelegte Datenfeld. Andernfalls gibt der Compiler eine Warnmeldung aus, wenn in den Indikatorbezeichner ein Parameter der Art uint eingestellt werden soll.
Im weiteren Verlauf prüfen wir, ob das gehandelte Kürzel in der „Marktübersicht“ vorhanden ist und ob es im Rahmen einer bestimmten Strategie mehr als einmal verwendet wurde:
//--- Check for the symbol in the Market Watch for(int i=0; i<Strategy_A; i++) { if(IsTrade_A[i]==false) continue; if(IsSymbolInMarketWatch(Symbol_A[i])==false) { Print(Symbol_A[i]," could not be found on the server!"); ExpertRemove(); } } //--- Check whether the symbol is used more than once if(Strategy_A>1) { for(int i=0; i<Strategy_A-1; i++) { if(IsTrade_A[i]==false) continue; for(int j=i+1; j<Strategy_A; j++) { if(IsTrade_A[j]==false) continue; if(Symbol_A[i]==Symbol_A[j]) { Print(Symbol_A[i]," is used more than once!"); ExpertRemove(); } } } } //--- The IsSymbolInMarketWatch() function bool IsSymbolInMarketWatch(string f_Symbol) { for(int s=0; s<SymbolsTotal(false); s++) { if(f_Symbol==SymbolName(s,false)) return(true); } return(false); }
Wenn die Kürzel richtig gewählt wurden, überprüfen wir jedes von ihnen auf Fehler in den Eingangsparametern, erstellen Bezeichner für die Indikatoren, ermitteln die zur Berechnung des Postens erforderlichen Daten und führen ggf. weitere für die gegebene Strategie charakteristischen Handlungen aus.
Das alles setzen wir in dem Arbeitsgang for um.
//--- General actions for(int i=0; i<Strategy_A; i++) { if(IsTrade_A[i]==false) continue; //--- Check for errors in input parameters //... //--- Set indicator handles BB_handle_high_A[i]=iBands(Symbol_A[i],Period_A[i],BBPeriod_A[i],BBShift_A[i],BBDeviation_A[i], PRICE_HIGH); if(BB_handle_high_A[i]<0) { Print("Failed to create a handle for Bollinger Bands based on High prices for ",Symbol_A[i]," . Handle=",INVALID_HANDLE, "\n Error=",GetLastError()); ExpertRemove(); } //... //--- Calculate data for the Lot //--- set the name of the symbol for which the information will be obtained SymbolInfo.Name(Symbol_A[i]); //--- minimum and maximum volume size in trading operations MinLot_A[i]=SymbolInfo.LotsMin(); MaxLot_A[i]=SymbolInfo.LotsMax(); //--- point value Point_A[i]=SymbolInfo.Point(); //--- contract size ContractSize_A[i]=SymbolInfo.ContractSize(); //--- Set some additional parameters }
Danach legen wir mithilfe des Objektes Trade_A der Klasse CTrade die Einstellungen für die Handelsoperationen der Strategie A fest.
//--- Set parameters for trading operations //--- set the magic number Trade_A.SetExpertMagicNumber(MagicNumber_A); //--- set the permissible slippage in points upon deal execution Trade_A.SetDeviationInPoints(Slippage_A); //--- order filling mode, use the mode that is allowed by the server Trade_A.SetTypeFilling(ORDER_FILLING_RETURN); //--- logging mode, it is advisable not to call this method as the class will set the optimal mode by itself Trade_A.LogLevel(1); //--- the function to be used for trading: true - OrderSendAsync(), false - OrderSend(). Trade_A.SetAsyncMode(true);
Wir wenden auf alle Strategien dasselbe Verfahren an, das heißt:
- wir kopieren die externen Variablen in Datenfelder,
- überprüfen, ob die Kürzel korrekt ausgewählt wurden,
- überprüfen sie auf Fehler, legen die Bezeichner für die Indikatoren an, berechnen die Daten für den Posten und machen alles, was die jeweilige Strategie erfordert, und
- legen die Parameter für die Handelsoperationen fest.
Zum Abschluss wäre es nicht das Schlechteste zu überprüfen, ob ein und dasselbe Kürzel für mehrere Strategien ausgewählt worden ist (es folgt ein Beispiel für zwei Strategien):
//--- Check whether one and the same symbol is used in several strategies for(int i=0; i<Strategy_A; i++) { if(IsTrade_A[i]==false) continue; for(int j=0; j<Strategy_B; j++) { if(IsTrade_B[j]==false) continue; if(Symbol_A[i]==Symbol_B[j]) { Print(Symbol_A[i]," is used in several strategies!"); ExpertRemove(); } } }
3. Handelsarbeitsgänge der Art „for“
So sieht das Gerüst aus Arbeitsgängen der Art for im Inneren der Funktion OnTimer() aus:
void OnTimer() { //--- Check if the terminal is connected to the trade server if(TerminalInfoInteger(TERMINAL_CONNECTED)==false) return; //--- Section A: Main loop of the FOR operator for strategy A ----------- for(int A=0; A<Strategy_A; A++) { //--- A.1: Check whether the symbol is allowed to be traded if(IsTrade_A[A]==false) continue; // terminate the current FOR iteration } //--- Section В: Main loop of the FOR operator for strategy В ----------- for(int B=0; B<Strategy_B; B++) { //--- B.1: Check whether the symbol is allowed to be traded if(IsTrade_B[B]==false) continue; // terminate the current FOR iteration } }
Wenn in einem Expert-System für nur eine Währung und lediglich eine Strategie eine Bedingung vorgesehen ist, bei deren Eintreten alle weiteren Berechnungen ausgesetzt werden, kommt der Operator return zur Anwendung. In diesem Fall müssen wir lediglich die aktuelle Iteration abbrechen und zur nächsten Iteration für das nachfolgende Kürzel übergehen. Dazu eignet sich am besten der Operator continue.
Wenn die Strategie in ein mehrwährungsfähiges Expert-System eingebettet werden soll, das über einen Arbeitsgang der Art for verfügt, innerhalb dessen die Bedingung für die Einstellung aller weiteren Berechnungen vorgeschrieben ist, kann folgendes Muster verwendet werden:
//--- Section N: Main loop of the FOR operator for strategy N ----------- for(int N=0; N<Strategy_N; N++) { //... bool IsInterrupt=false; for(int i=0; i<Number; i++) { if(...) // terminate all calculations { IsInterrupt=true; break; } } if(IsInterrupt=true) continue; // terminate the current FOR iteration //... }
Nach der Erstellung des Gerüstes aus for-Arbeitsgängen übertragen wir lediglich den Code aus den anderen Expert-Systemen dorthin und ersetzen einige Variablen durch Elemente aus den Datenfeldern.
Wir ersetzen beispielsweise die vordefinierten Variablen _Symbol durch Symbol_A[i] bzw. _Point durch Point_A[i]. Die Werte dieser Variablen sind für dieses Kürzel charakteristisch, weshalb sie bei der Bereitstellung in die Datenfelder kopiert worden sind.
Wir ermitteln beispielsweise den Wert des Indikators:
//--- A.3: Lower band of BB calculated based on High prices if(CopyBuffer(BB_handle_high_A[A],LOWER_BAND,BBShift_A[A],1,BB_lower_band_high)<=0) continue; // terminate the current FOR iteration ArraySetAsSeries(BB_lower_band_high,true);
Das Schließen der Kaufposition programmieren wir wie folgt:
//--- A.7.1: Calculate the current Ask and Bid prices SymbolInfo.Name(Symbol_A[A]); SymbolInfo.RefreshRates(); double Ask_price=SymbolInfo.Ask(); double Bid_price=SymbolInfo.Bid(); if(PositionSelect(Symbol_A[A])) { //--- A.7.2: Closing a BUY position if(PositionInfo.PositionType()==POSITION_TYPE_BUY) { if(Bid_price>=BB_lower_band_high[0] || DealNumber_A[A]==0) { if(!Trade_A.PositionClose(Symbol_A[A])) { Print("Failed to close the Buy ",Symbol_A[A]," position. Code=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // terminate the current FOR iteration } else { Print("The Buy ",Symbol_A[A]," position closed successfully. Code=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // terminate the current FOR iteration } } } //... }
Eröffnen einer Kaufposition:
//--- A.9.1: for a Buy if(Ask_price<=BB_lower_band_low[0]) { //... //--- A.9.1.3: Execute a deal if(!Trade_A.Buy(OrderLot,Symbol_A[A])) { Print("The Buy ",Symbol_A[A]," has been unsuccessful. Code=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // terminate the current FOR iteration } else { Print("The Buy ",Symbol_A[A]," has been successful. Code=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // terminate the current FOR iteration } }
Bei der Außerfunktionssetzung (Bereinigung) des Expert-Systems dürfen wir nicht vergessen, die Erzeugung von Zeitgeberereignissen auszusetzen und die Indikatorbezeichner zu löschen.
4. Testergebnisse
Nachdem wir das Expert-System fertiggestellt haben, überprüfen wir es einzeln zu jeder Strategie und jedem Kürzel, und vergleichen die Ergebnisse mit dem, das wir erhalten, wenn wir es im gleichzeitigen Handel mit allen Strategien und Kürzeln testen.
Wir setzen dabei voraus, dass der Anwender bereits die besten Werte für die Eingangsparameter ausgewählt hat.
Es folgen die Einstellungen für das Strategieprüfprogramm:
Abb. 2. Einstellungen für das Strategieprüfprogramm
Prüfergebnis der Strategie A für das Kürzel EURUSD:
Abb. 3. Prüfergebnis der Strategie A für das Kürzel EURUSD
Prüfergebnis der Strategie A für das Kürzel GBPUSD:
Abb. 4. Prüfergebnis der Strategie A für das Kürzel GBPUSD
Prüfergebnis der Strategie B für das Kürzel AUDUSD:
Abb. 5. Prüfergebnis der Strategie B für das Kürzel AUDUSD
Prüfergebnis der Strategie B für das Kürzel EURJPY:
Abb. 6. Prüfergebnis der Strategie В für das Kürzel EURJPY
Prüfergebnis mit allen Strategien und Kürzeln
Abb. 7. Prüfergebnis mit allen Strategien und Kürzeln
Fazit
Wir haben ein praktisches und gleichzeitig einfaches Modell für ein systemübergreifendes mehrwährungsfähiges automatisches Handelssystem erhalten, in dem sich nahezu jede Strategie unterbringen lässt.
Ein solches „Expert-System“ trägt dazu bei, dass die Auswirkungen des Handels in Bezug auf alle Strategien besser eingeschätzt werden können. Es kann auch in dem Fall von Nutzen sein, wenn auf einem Konto nur ein automatisches Handelssystem eingesetzt werden kann. Zum Zweck des Studiums des oben dargelegten Materials ist der Quellcode des Expert-Systems im Anhang zu diesem Beitrag beigefügt.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/770
![Ein Indikator zum Anlegen eines Kagi-Diagramms](https://c.mql5.com/2/0/LOGO.png)
![Das MQL5-Kochbuch: Entwicklung eines mehrwährungsfähigen Kursschwankungsindikators in MQL5](https://c.mql5.com/2/0/avatar1__1.png)
![Ständige Terminkontrakte in MetaTrader 5](https://c.mql5.com/2/0/Futures_MQL5.png)
![Das MQL5-Kochbuch: Steuerelemente des Indikatorunterfensters - Die Bildlaufleiste](https://c.mql5.com/2/0/avatar__10.png)
![MQL5 - Sprache von Handelsstrategien, eingebaut ins Kundenterminal MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.