
Aufbau des Kerzenmodells Trend Constraint (Teil 9): Expert Advisor für mehrere Strategien (II)
Inhalt:
- Einführung,
- Was ist der Donchian-Kanal?
- Zugriff auf den Donchian-Kanal in MetaTrader 5
- Zugriff auf den Quellcode des Donchian-Kanals in MetaEditor
- Implementierung von Donchian-Kanal-Strategien in Trend Constraint Expert.
- Tests und Ergebnisse
- Schlussfolgerung
Einführung
Im 20. Jahrhundert entwickelte Richard Donchian durch seine Studien der Finanzmärkte eine Trendfolgestrategie, die später zu den Donchian-Kanälen weiterentwickelt wurde. Wir haben seine Arbeit in einem früheren Artikel kurz erörtert, aber heute wollen wir uns auf die Umsetzung der mit seiner Theorie verbundenen Strategien konzentrieren. Verschiedenen Quellen zufolge wird davon ausgegangen, dass die Kanäle in ihrem Rahmen mehrere Strategien umfassen. Die Fülle der Literatur über Donchian-Kanäle deutet darauf hin, dass diese Theorie im modernen Handel nach wie vor wirksam ist. Durch die Integration der Donchian Channel-Strategien wollen wir die Möglichkeiten für unseren Trend Constraint Expert erweitern und sowohl seine Rentabilität als auch seine Anpassungsfähigkeit an unterschiedliche Marktbedingungen verbessern.
Einige beliebte Strategien, die auf dem Donchian-Kanal basieren und online verfügbar sind, sind unter anderem die Breakout-Strategie, die Crawl-Strategie und die Mean-Reversion-Strategie. Namhafte Händler, wie Rayner Teo, haben auch Bildungsinhalte erstellt, die den Händlern zeigen, wie sie diese Kanäle effektiv nutzen können.
Donchian Channels sind als kostenlose Indikatoren auf der MetaTrader 5 Plattform verfügbar, was uns für dieses Projekt einen erheblichen Vorteil verschafft. Dieser Zugang ermöglicht es uns, den Quellcode des Indikators zu nutzen und tiefere Einblicke in seine Struktur zu gewinnen, was die Anpassung an unseren Trend Constraint Expert erleichtert. Da unser Code immer komplexer wird, werden wir die neue Strategie unabhängig entwickeln, bevor wir sie in das Hauptprogramm integrieren. In den kommenden Abschnitten werden wir unser Verständnis der Theorien vertiefen und unseren Algorithmus weiter verbessern.
Was ist der Donchian-Kanal?
Der Donchian-Kanal ist ein Indikator für die technische Analyse, der aus drei Linien besteht, nämlich dem oberen Band, der mittleren Linie und dem unteren Band, die dazu dienen, höhere Höchst- und niedrigere Tiefststände während der Kursbewegung darzustellen. Wie bereits erwähnt, geht der Dank an Richard Davoud Donchian, einen Pionier auf dem Gebiet des trendfolgenden Handels. Im Folgenden werden die drei Zeilen kurz skizziert: - Oberes Band: Diese Linie stellt den höchsten Stand über einen bestimmten Zeitraum dar (z. B. die letzten 20 Zeiträume).
- Unteres Band: Diese Linie zeigt den tiefsten Stand im gleichen Zeitraum an.
- Mittlere Linie: Diese Linie, die oft als Durchschnitt der oberen und unteren Bande berechnet wird, wird manchmal als Referenzpunkt verwendet.
Linien des Donchian-Kanals
Zugriff auf den Donchian-Kanal in MetaTrader 5
Der Zugriff erfolgt in der Regel über das Navigator-Fenster unter der Registerkarte Indikatoren, wie in der folgenden Abbildung dargestellt.
MetaTrader 5 Navigator
Sobald Sie darauf zugreifen, können Sie den Donchian-Kanal auf die Karte ziehen, in der Sie ihn verwenden möchten, wie in der folgenden Abbildung gezeigt. In diesem Beispiel verwenden wir die Standardkanaleinstellungen für den Volatilitätsindex 150 (1s).
Hinzufügen eines Donchian-Kanals zum Chart in MetaTrader 5
Der Grund, warum wir den Indikator zunächst auf das Chart anwenden, ist die Untersuchung der Beziehung zwischen der Preisbewegung und dem Kanal. Dies hilft uns, die Regeln zu verstehen, bevor wir mit der Entwicklung des Algorithmus beginnen. Als Nächstes zeigen wir Ihnen, wie Sie in MetaEditor 5 auf den Quellcode des Indikators zugreifen können.
Zugriff auf den Quellcode des Donchian-Kanals in MetaEditor
Um auf die Quelldatei zur Bearbeitung in MetaEditor 5 zuzugreifen, öffnen Sie das Navigator-Fenster und suchen Sie den Indikator im Unterverzeichnis „Free Indicators“, genau wie auf der MetaTrader 5-Plattform. Der entscheidende Unterschied besteht darin, dass wir hier mit der Quelldatei und nicht mit der kompilierten Version arbeiten. Doppelklicken Sie auf die Datei, um den Code anzuzeigen. Die nachstehenden Abbildungen erleichtern die Nachverfolgung.
Zugriff auf den Quellcode des Donchain-Kanals in MetaEditor.
Implementierung von Donchian-Kanal-Strategien in Trend Constraint Expert
Es ist wichtig, die Leitparameter hervorzuheben, die unsere Diskussionen bestimmen und die Regeln für die Einführung neuer Instrumente festlegen. Wir legen immer von Anfang an die Randbedingungen fest. Wir kaufen zum Beispiel nur, wenn es eine steigende D1-Kerze gibt, und wir verkaufen, wenn es eine fallende D1-Kerze gibt. In diesem Sinne werden wir zunächst die einschränkenden Bedingungen ermitteln, bevor wir nach Auftragschancen suchen, die sich aus dem Kanal-Setup ergeben und mit dem Markttrend übereinstimmen. In einem steigenden D1-Szenario werden wir uns zum Beispiel auf Folgendes konzentrieren: - Wenn der Kurs die untere Grenze des Kanals berührt, werden Kaufaufträge mit höherer Gewinnwahrscheinlichkeit erteilt.
- Abprall der mittleren Linie mit mittlerer Gewinnwahrscheinlichkeit
- Ausbruch der oberen Begrenzung, bei geringer Gewinnwahrscheinlichkeit
Ich habe die drei Ideen in der folgenden Abbildung dargestellt.
Donchian-Kanal-Strategien
In dieser Präsentation werde ich die Breakout-Technik verwenden, die die Bedingungen überwacht, wenn der Marktpreis außerhalb der äußeren Grenzen des Kanals schließt. Schauen wir uns nun den Quellcode des Indikators an und identifizieren die relevanten Puffer.
Vorschau auf den Quellcode des Indikators
Der Standardindikator Donchian Channel ist auf der MetaTrader 5-Plattform verfügbar. Sie können auch direkt in MetaEditor 5 darauf zugreifen, indem Sie die zuvor beschriebenen Methoden verwenden.//+------------------------------------------------------------------+ //| Donchian Channel.mq5 | //| Copyright 2009-2024, MetaQuotes Ltd | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2009-2024, MetaQuotes Ltd" #property link "http://www.mql5.com" #property description "Donchian Channel" //--- #property indicator_chart_window #property indicator_buffers 3 #property indicator_plots 3 #property indicator_type1 DRAW_LINE #property indicator_color1 clrBlue #property indicator_type2 DRAW_LINE #property indicator_color2 clrGray #property indicator_type3 DRAW_LINE #property indicator_color3 clrRed //--- labels #property indicator_label1 "Upper Donchian" #property indicator_label2 "Middle Donchian" #property indicator_label3 "Lower Donchian" //--- input parameter input int InpDonchianPeriod=20; // period of the channel input bool InpShowLabel =true; // show price of the level //--- indicator buffers double ExtUpBuffer[]; double ExtMdBuffer[]; double ExtDnBuffer[]; //--- unique prefix to identify indicator objects string ExtPrefixUniq; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- define buffers SetIndexBuffer(0, ExtUpBuffer); SetIndexBuffer(1, ExtMdBuffer); SetIndexBuffer(2, ExtDnBuffer); //--- set a 1-bar offset for each line PlotIndexSetInteger(0, PLOT_SHIFT, 1); PlotIndexSetInteger(1, PLOT_SHIFT, 1); PlotIndexSetInteger(2, PLOT_SHIFT, 1); //--- indicator name IndicatorSetString(INDICATOR_SHORTNAME, "Donchian Channel"); //--- number of digits of indicator value IndicatorSetInteger(INDICATOR_DIGITS, _Digits); //--- prepare prefix for objects string number=StringFormat("%I64d", GetTickCount64()); ExtPrefixUniq=StringSubstr(number, StringLen(number)-4); ExtPrefixUniq=ExtPrefixUniq+"_DN"; Print("Indicator \"Donchian Channels\" started, prefix=", ExtPrefixUniq); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if the indicator has previously been calculated, start from the bar preceding the last one int start=prev_calculated-1; //--- if this is the first calculation of the indicator, then move by InpDonchianPeriod bars form the beginning if(prev_calculated==0) start=InpDonchianPeriod+1; //--- calculate levels for all bars in a loop for(int i=start; i<rates_total; i++) { //--- get max/min values for the last InpDonchianPeriod bars int highest_bar_index=ArrayMaximum(high, i-InpDonchianPeriod+1, InpDonchianPeriod); int lowest_bar_index=ArrayMinimum(low, i-InpDonchianPeriod+1, InpDonchianPeriod);; double highest=high[highest_bar_index]; double lowest=low[lowest_bar_index]; //--- write values into buffers ExtUpBuffer[i]=highest; ExtDnBuffer[i]=lowest; ExtMdBuffer[i]=(highest+lowest)/2; } //--- draw labels on levels if(InpShowLabel) { ShowPriceLevels(time[rates_total-1], rates_total-1); ChartRedraw(); } //--- succesfully calculated return(rates_total); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- delete all our graphical objects after use Print("Indicator \"Donchian Channels\" stopped, delete all objects with prefix=", ExtPrefixUniq); ObjectsDeleteAll(0, ExtPrefixUniq, 0, OBJ_ARROW_RIGHT_PRICE); ChartRedraw(0); } //+------------------------------------------------------------------+ //| Show prices' levels | //+------------------------------------------------------------------+ void ShowPriceLevels(datetime time, int last_index) { ShowRightPrice(ExtPrefixUniq+"_UP", time, ExtUpBuffer[last_index], clrBlue); ShowRightPrice(ExtPrefixUniq+"_MD", time, ExtMdBuffer[last_index], clrGray); ShowRightPrice(ExtPrefixUniq+"_Dn", time, ExtDnBuffer[last_index], clrRed); } //+------------------------------------------------------------------+ //| Create or Update "Right Price Label" object | //+------------------------------------------------------------------+ bool ShowRightPrice(const string name, datetime time, double price, color clr) { if(!ObjectCreate(0, name, OBJ_ARROW_RIGHT_PRICE, 0, time, price)) { ObjectMove(0, name, 0, time, price); return(false); } //--- make the label size adaptive long scale=2; if(!ChartGetInteger(0, CHART_SCALE, 0, scale)) { //--- output an error message to the Experts journal Print(__FUNCTION__+", ChartGetInteger(CHART_SCALE) failed, error = ", GetLastError()); } int width=scale>1 ? 2:1; // if chart scale > 1, then label size = 2 ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, width); ObjectSetInteger(0, name, OBJPROP_BACK, false); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, name, OBJPROP_SELECTED, false); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); ObjectSetInteger(0, name, OBJPROP_ZORDER, 0); return(true); } //+------------------------------------------------------------------+
Der obige nutzerdefinierte Code implementiert den Donchian-Kanal. Er berechnet und zeigt drei Linien an: die obere Kanallinie (die den höchsten Stand in einem bestimmten Zeitraum repräsentiert), die untere Kanallinie (die den niedrigsten Stand im gleichen Zeitraum repräsentiert) und eine mittlere Linie (der Durchschnitt aus der oberen und unteren Linie). Der Indikator wurde entwickelt, um potenzielle Ausbruchspunkte zu visualisieren, mit anpassbaren Eingabeparametern für die Kanalperiode und der Option, Preisetiketten auf dem Chart anzuzeigen. Der Code enthält Initialisierungsfunktionen zum Einrichten von Indikatorpuffern und -eigenschaften, eine Berechnungsschleife, die die Kanallinien für jeden Balken im Chart aktualisiert, sowie Funktionen zur Verwaltung von grafischen Objekten und Preisetiketten. Insgesamt bietet es Händlern ein Instrument, mit dem sie Trends und potenzielle Handelsmöglichkeiten auf der Grundlage historischer Kursniveaus erkennen können.
Zum besseren Verständnis wollen wir die obigen Codeschnipsel aufschlüsseln und unsere Puffer für die weitere Entwicklung im nächsten Abschnitt identifizieren.
Deklaration der Puffer:
Es werden drei Puffer ExtUpBuffer[], ExtMdBuffer[] und ExtDnBuffer[] deklariert, die jeweils die oberen, mittleren und unteren Werte des Donchian-Kanals aufnehmen.
double ExtUpBuffer[]; double ExtMdBuffer[]; double ExtDnBuffer[];
Buffer Setup in OnInit:
Die Funktion SetIndexBuffer verknüpft die Zeichnungen auf dem Chart (Linien) mit den Puffern, sodass sie im Chart gezeichnet und aktualisiert werden können.
SetIndexBuffer(0, ExtUpBuffer); SetIndexBuffer(1, ExtMdBuffer); SetIndexBuffer(2, ExtDnBuffer);
Berechnung der Pufferwerte in OnCalculate:
Dieser Code berechnet den Höchst-, Tiefst- und Durchschnittspreis über den definierten Zeitraum und speichert sie in den entsprechenden Puffern für jeden Balken.
for(int i=start; i<rates_total; i++) { //--- calculate highest and lowest for the Donchian period int highest_bar_index = ArrayMaximum(high, i-InpDonchianPeriod+1, InpDonchianPeriod); int lowest_bar_index = ArrayMinimum(low, i-InpDonchianPeriod+1, InpDonchianPeriod); double highest = high[highest_bar_index]; double lowest = low[lowest_bar_index]; //--- assign values to buffers ExtUpBuffer[i] = highest; ExtDnBuffer[i] = lowest; ExtMdBuffer[i] = (highest + lowest) / 2; }
Um ein Kaufsignal zu generieren, verwendet die Strategie den oberen Puffer (ExtUpBuffer) und löst einen Kauf aus, wenn der Kurs über der oberen Donchian-Linie schließt. Umgekehrt wird ein Verkaufssignal ausgelöst, wenn der Kurs unterhalb der unteren Donchian-Linie schließt, die durch den unteren Puffer (ExtDnBuffer) definiert ist. Darüber hinaus kann der mittlere Kanal (ExtMdBuffer) als Filter fungieren, der die Strategie verfeinert, indem er Kaufgeschäfte auf Fälle beschränkt, in denen der Preis über dem mittleren Kanal liegt, was auf einen stärkeren Aufwärtstrend hindeutet. Mit diesem Einschätzung im Hinterkopf bin ich zuversichtlich, dass wir jetzt mit der Entwicklung unseres Expert Advisors (EA) fortfahren können.
Code-Entwicklung
Die Verfügbarkeit des Donchian Channel als integrierter Indikator vereinfacht unsere Aufgabe, da wir einen Expert Advisor (EA) entwickeln können, der sich auf die Puffer des Indikators konzentriert, um Signale für die Handelsausführung zu generieren. Wie bereits erwähnt, werden wir der Klarheit halber zunächst einen auf dem Donchian-Kanal basierenden EA entwickeln, bevor wir ihn in unseren Trend Constraint Expert integrieren. Heute konzentrieren wir uns auf die Ausbruchsstrategie unter Verwendung des Donchian-Kanals. Die Ausbruchsbedingung ist einfach: Sie tritt ein, wenn der Kurs über die Extrembereiche des Kanals hinaus schließt. Wir beziehen uns auf das frühere Bild, in dem wir verschiedene Strategien im Detail erklärt haben.
Um zu beginnen, erstellen wir eine neue Datei in MetaEditor 5, wie in der Abbildung unten gezeigt. Ich habe sie „BreakoutEA“ genannt, da wir uns in erster Linie auf diese Ausbruchsstrategie konzentrieren werden.
Beginn eines neuen EA im MetaEditor
Der Entstehungsprozess ist in fünf große Abschnitte unterteilt, die im Folgenden Schritt für Schritt verfolgen können, um die gesamte Entwicklung zu verstehen. Wenn wir den EA starten, beginnen wir zunächst mit der Basisvorlage und lassen die anderen Teile unangekreuzt. Unterhalb dieser Vorlage werden die wichtigsten Komponenten erläutert, die am Ende zusammenkommen.
In dieser grundlegenden Vorlage finden Sie wichtige Eigenschaften, wie die Richtlinie(#property strict). Diese Direktive stellt sicher, dass der Compiler die korrekte Verwendung von Datentypen erzwingt und hilft so, potenzielle Programmierfehler aufgrund von Typinkongruenzen zu vermeiden. Ein weiterer wichtiger Aspekt ist die Einbeziehung der Handelsbibliothek, die die notwendigen Werkzeuge für eine effiziente Verwaltung der Handelsoperationen bereitstellt. Diese Schritte bilden eine solide Grundlage für den Entwicklungsprozess.
//+------------------------------------------------------------------+ //| BreakoutEA.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
1. Initialisierung des Expert Advisors
Im Initialisierungssegment werden die Eingabeparameter für den EA definiert. Mit diesen Parametern können wir den EA nach unseren Handelsvorlieben konfigurieren. Zu den wichtigen Eingaben gehören die Donchian-Kanal-Periode, das Risiko-Ertrags-Verhältnis, die Losgröße für die Handelsgeschäfte und die Pip-Werte für Stop-Loss und Take-Profit.
// Input parameters input int InpDonchianPeriod = 20; // Period for Donchian Channel input double RiskRewardRatio = 1.5; // Risk-to-reward ratio input double LotSize = 0.1; // Default lot size for trading input double pipsToStopLoss = 15; // Stop loss in pips input double pipsToTakeProfit = 30; // Take profit in pips // Indicator handle storage int handle; string indicatorKey; // Expert initialization function int OnInit() { // Create a unique key for the indicator based on the symbol and period indicatorKey = StringFormat("%s_%d", Symbol(), InpDonchianPeriod); // Load the Donchian Channel indicator handle = iCustom(Symbol(), Period(), "Free Indicators\\Donchian Channel", InpDonchianPeriod); // Check if the indicator loaded successfully if (handle == INVALID_HANDLE) { Print("Failed to load the indicator. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; }
Auf der Grundlage des Handelssymbols und des definierten Zeitraums wird ein eindeutiger Schlüssel erstellt, der gewährleistet, dass jede Instanz des Indikators unterschieden werden kann. Die Funktion iCustom() dient zum Laden des Donchian-Channel-Indikators, wobei der Pfad im MetaTrader-Verzeichnis (Free Indicators\\Donchian Channel) angegeben wird. Schlägt das Laden fehl (angezeigt durch ein INVALID_HANDLE), wird eine Fehlermeldung ausgegeben, und die Initialisierung schlägt fehl, sodass eine weitere Ausführung ohne die erforderlichen Indikatordaten nicht möglich ist. Es ist wichtig, den Speicherort anzugeben, da der Indikator nicht im Stammordner des Indikators vorhanden ist und die Nichtangabe zu einem Fehler führt, wie unten gezeigt. In den meisten Fällen wird der EA nicht ausgeführt, wenn der Indikator nicht geladen werden kann.
//Typical Journal log when the EA fails to locate an indicator in the root indicators storage. 2024.10.20 08:49:04.117 2022.01.01 00:00:00 cannot load custom indicator 'Donchian Channel' [4802] 2024.10.20 08:49:04.118 2022.01.01 00:00:00 indicator create error in 'DonchianEA.mq5' (1,1) 2024.10.20 08:49:04.118 OnInit critical error 2024.10.20 08:49:04.118 tester stopped because OnInit failed
2. Bereinigung und Deinitialisierung
Das Bereinigungssegment ist für die Freigabe von Ressourcen verantwortlich, die der EA in Anspruch genommen hat. Dies geschieht in der Funktion OnDeinit(), die aufgerufen wird, wenn der EA entfernt wird oder wenn MetaTrader 5 heruntergefahren wird. Die Funktion stellt sicher, dass das Indikator-Handle mit IndicatorRelease() freigegeben wird. Eine gründliche Bereinigung der Ressourcen ist wichtig, um Speicherlecks zu vermeiden und die Gesamtleistung der Plattform zu erhalten.
// Expert deinitialization function void OnDeinit(const int reason) { // Release the indicator handle to free up resources IndicatorRelease(handle); }
3. Hauptausführungslogik
Die Hauptausführungslogik befindet sich in der Funktion OnTick(), die bei jedem Markttick oder jeder Preisänderung ausgelöst wird. In dieser Funktion wird mit Hilfe der Funktion PositionsTotal() geprüft, ob es derzeit offene Positionen gibt. Wenn keine Positionen offen sind, fährt das Programm mit der Auswertung der Handelsbedingungen fort, indem es eine separate Funktion aufruft. Diese Struktur verhindert, dass mehrere Handelsgeschäfte auf einmal eröffnet werden, was zu einem Overtrading führen könnte.
// Main execution function with block-based control void OnTick() { // Check if any positions are currently open if (PositionsTotal() == 0) { CheckTradingConditions(); } }
4. Bewertung der Handelsbedingungen
In diesem Segment überprüft der EA die Marktbedingungen anhand der oberen und unteren Bänder des Donchian-Kanals. Die Größe der Indikatorpuffer wird angepasst, um die neuesten Daten aufzunehmen. Die Funktion CopyBuffer() ruft die neuesten Werte aus dem Donchian-Kanal ab.
// Check trading conditions based on indicator buffers void CheckTradingConditions() { double ExtUpBuffer[], ExtDnBuffer[]; // Buffers for upper and lower Donchian bands // Resize buffers to hold the latest data ArrayResize(ExtUpBuffer, 2); ArrayResize(ExtDnBuffer, 2); // Get the latest values from the Donchian Channel if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0) { Print("Error reading indicator buffer. Error: ", GetLastError()); return; } // Get the close price of the current candle double closePrice = iClose(Symbol(), Period(), 0); // Buy condition: Closing price is above the upper Donchian band if (closePrice > ExtUpBuffer[1]) { double stopLoss = closePrice - pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice + pipsToTakeProfit * _Point; // Calculate take profit OpenBuy(LotSize, stopLoss, takeProfit); } // Sell condition: Closing price is below the lower Donchian band if (closePrice < ExtDnBuffer[1]) { double stopLoss = closePrice + pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice - pipsToTakeProfit * _Point; // Calculate take profit OpenSell(LotSize, stopLoss, takeProfit); } }
Man erhält den aktuellen Schlusskurs, der für die Bewertung von Handelssignalen entscheidend ist. Die Handelsbedingungen sind so definiert, dass ein Kaufauftrag ausgelöst wird, wenn der Schlusskurs das obere Band überschreitet, während ein Verkaufsauftrag erteilt wird, wenn der Kurs unter das untere Band fällt. Stop-Loss und Take-Profit werden auf der Grundlage nutzerdefinierter Pip-Werte berechnet, um das Risiko effektiv zu steuern.
5. Funktionen der Auftragsvergabe
Die Funktionen zur Auftragsvergabe sorgen für die Ausführung von Kauf- und Verkaufsgeschäften. Jede Funktion versucht, einen Handel mit Hilfe von Methoden der Klasse CTrade zu platzieren, was die Transaktionsverwaltung vereinfacht. Nach dem Versuch, einen Handel auszuführen, prüft das Programm, ob der Auftrag erfolgreich war. Schlägt sie fehl, wird eine Fehlermeldung ausgegeben, um den Händler über den Fehler zu informieren. Diese Funktionen kapseln die Handelslogik und bieten eine klare Schnittstelle für die Platzierung von Aufträgen auf der Grundlage der zuvor festgelegten Bedingungen.
// Open a buy order void OpenBuy(double lotSize, double stopLoss, double takeProfit) { // Attempt to open a buy order if (trade.Buy(lotSize, Symbol(), 0, stopLoss, takeProfit, "Buy Order")) { Print("Buy order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open buy order. Error: ", GetLastError()); } } // Open a sell order void OpenSell(double lotSize, double stopLoss, double takeProfit) { // Attempt to open a sell order if (trade.Sell(lotSize, Symbol(), 0, stopLoss, takeProfit, "Sell Order")) { Print("Sell order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open sell order. Error: ", GetLastError()); } }
Unser Donchian Channel Breakout EA ist jetzt vollständig entwickelt und bereit, alles an einem Ort.
//+----------------------------------------------------------------------+ //| BreakoutEA.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com/en/users/billionaire2024/seller | //+----------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com/en/users/billionaire2024/seller" #property version "1.00" #property strict #include <Trade\Trade.mqh> // Include the trade library // Input parameters input int InpDonchianPeriod = 20; // Period for Donchian Channel input double RiskRewardRatio = 1.5; // Risk-to-reward ratio input double LotSize = 0.1; // Default lot size for trading input double pipsToStopLoss = 15; // Stop loss in pips input double pipsToTakeProfit = 30; // Take profit in pips // Indicator handle storage int handle; string indicatorKey; double ExtUpBuffer[]; // Upper Donchian buffer double ExtDnBuffer[]; // Lower Donchian buffer // Trade instance CTrade trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { indicatorKey = StringFormat("%s_%d", Symbol(), InpDonchianPeriod); // Create a unique key for the indicator handle = iCustom(Symbol(), Period(), "Free Indicators\\Donchian Channel", InpDonchianPeriod); if (handle == INVALID_HANDLE) { Print("Failed to load the indicator. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release the indicator handle IndicatorRelease(handle); } //+------------------------------------------------------------------+ //| Main execution function with block-based control | //+------------------------------------------------------------------+ void OnTick() { // Check if any positions are currently open if (PositionsTotal() == 0) { CheckTradingConditions(); } } //+------------------------------------------------------------------+ //| Check trading conditions based on indicator buffers | //+------------------------------------------------------------------+ void CheckTradingConditions() { // Resize buffers to get the latest data ArrayResize(ExtUpBuffer, 2); ArrayResize(ExtDnBuffer, 2); // Get the latest values from the Donchian Channel if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0) { Print("Error reading indicator buffer. Error: ", GetLastError()); return; } // Get the close price of the current candle double closePrice = iClose(Symbol(), Period(), 0); // Buy condition: Closing price is above the upper Donchian band if (closePrice > ExtUpBuffer[1]) { double stopLoss = closePrice - pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice + pipsToTakeProfit * _Point; // Calculate take profit OpenBuy(LotSize, stopLoss, takeProfit); } // Sell condition: Closing price is below the lower Donchian band if (closePrice < ExtDnBuffer[1]) { double stopLoss = closePrice + pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice - pipsToTakeProfit * _Point; // Calculate take profit OpenSell(LotSize, stopLoss, takeProfit); } } //+------------------------------------------------------------------+ //| Open a buy order | //+------------------------------------------------------------------+ void OpenBuy(double lotSize, double stopLoss, double takeProfit) { if (trade.Buy(lotSize, Symbol(), 0, stopLoss, takeProfit, "Buy Order")) { Print("Buy order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open buy order. Error: ", GetLastError()); } } //+------------------------------------------------------------------+ //| Open a sell order | //+------------------------------------------------------------------+ void OpenSell(double lotSize, double stopLoss, double takeProfit) { if (trade.Sell(lotSize, Symbol(), 0, stopLoss, takeProfit, "Sell Order")) { Print("Sell order placed: Symbol = ", Symbol(), ", LotSize = ", lotSize); } else { Print("Failed to open sell order. Error: ", GetLastError()); } } //+------------------------------------------------------------------+
Testen wir den Breakout EA, bevor wir ihn in den Hauptcode des Trend Constraints einbauen. Es ist wichtig anzumerken, dass dies nicht die endgültige Version ist, da wir die auf unsere Gesamtziele abgestimmte Logik noch nicht implementiert haben.
Hinzufügen von BreakoutEA zum Chart
Klicken Sie mit der rechten Maustaste auf die Expert Advisor-Liste und wählen Sie „Testen“, um das Testfenster zu öffnen. Von dort aus können Sie den BreakoutEA für den Test auswählen und einstellen. Sehen Sie sich die Vorstellung unten an.
Der BreakoutEA im Strategietester
Gut gemacht! Wir haben Aufträge erfolgreich ausgeführt, was ein großer Erfolg ist. Jetzt können wir diese Grundlage nutzen, um die Rentabilität zu verbessern und unnötige Geschäfte herauszufiltern. Dies unterstreicht die Bedeutung der nächsten Phase, in der wir Einschränkungen einbauen werden, um weniger wahrscheinliche Geschäfte auszuschließen.Einbindung in Trend Constraint Expert
Bei der Verschmelzung zweier EA-Codes werden die Funktionen beider Seiten kombiniert, wobei die gemeinsamen Funktionen in der endgültigen EA primär sind. Darüber hinaus werden einzigartige Funktionen von jedem EA den Gesamtumfang und die Fähigkeiten des zusammengeführten Codes erweitern. So gibt es beispielsweise Eigenschaften, die sowohl im Trend Constraint Expert als auch im Breakout EA vorhanden sind, und wir werden sie in einem Programm zusammenfassen. Im folgenden Codeschnipsel werden diese gemeinsamen Eigenschaften hervorgehoben.
// We merge it to one #property strict #include <Trade\Trade.mqh> // Include the trade library
Lassen Sie uns nun die Eingliederung vornehmen. Bei der Verschmelzung von zwei oder mehr Strategien zu einem einzigen Expert Advisor (EA) bleiben die primären Funktionen, die für die gesamte Handelslogik von zentraler Bedeutung sind, wie OnInit(), OnTick() und Positionsmanagementfunktionen (wie OpenBuy() und OpenSell()). Diese Funktionen bilden das Herzstück des EA und sind für die Initialisierung der Indikatoren, die Marktanalyse in Echtzeit und die Orderplatzierung zuständig.
In der Zwischenzeit werden die Funktionen der Donchian-Channel-Breakout-Strategie und der RSI mit gleitendem Durchschnitt-Trendfolgestrategie in Trend Constraint Expert zu Erweiterungen des bestehenden Programms, die als eigene Bedingungen in die Funktion OnTick() integriert werden. Der EA wertet sowohl die Ausbruchssignale des Donchian-Kanals als auch die Trendsignale des RSI und der gleitenden Durchschnitte gleichzeitig aus und kann so umfassender auf die Marktbedingungen reagieren.
Durch die Integration dieser unabhängigen Funktionen verbessert der EA seine Entscheidungsfähigkeiten, was zu einer robusteren Handelsstrategie führt, die sich an unterschiedliche Marktdynamiken anpassen kann.
1. Die Initialisierungsfunktionen
Die Initialisierungsfunktion (OnInit()) ist von entscheidender Bedeutung, da sie die notwendigen Indikatoren für beide Handelsstrategien, die der Expert Advisor verwenden wird, einrichtet. Diese Funktion wird aufgerufen, wenn der EA zum ersten Mal geladen wird, und stellt sicher, dass alle wesentlichen Komponenten bereit sind, bevor die Handelsoperationen beginnen. Er initialisiert den RSI (Relative Strength Index) und den Donchian Channel Indikator. Wenn einer dieser Indikatoren nicht ordnungsgemäß initialisiert werden kann, gibt die Funktion einen Fehlerstatus zurück, sodass das Handelssystem nicht läuft und potenzielle Marktrisiken vermieden werden.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize RSI handle rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE); if (rsi_handle == INVALID_HANDLE) { Print("Failed to create RSI indicator handle"); return INIT_FAILED; } // Create a handle for the Donchian Channel handle = iCustom(Symbol(), Period(), "Free Indicators\\Donchian Channel", InpDonchianPeriod); if (handle == INVALID_HANDLE) { Print("Failed to load the Donchian Channel indicator. Error: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; }
2. Hauptausführungslogik
Die Hauptausführungslogik wird in der Funktion OnTick() abgewickelt, die jedes Mal aufgerufen wird, wenn es einen Markttick gibt. Diese Funktion dient als Herzstück des EA und steuert die Bewertung verschiedener Handelsstrategien als Reaktion auf sich ändernde Marktbedingungen. Sie ruft nacheinander die Funktionen auf, die für die Überprüfung der Trendfolgestrategie und der Ausbruchsstrategie zuständig sind. Darüber hinaus enthält es eine Prüfung auf abgelaufene Aufträge, sodass der EA ein effektives Risikomanagement betreiben kann, indem er sicherstellt, dass keine veralteten Positionen aktiv bleiben.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Execute both strategies independently on each tick CheckTrendConstraintTrading(); CheckBreakoutTrading(); CheckOrderExpiration(); // Check for expired Trend Following orders }
3. Modulare Handelskonditionsfunktionen
Trendfolge-Strategie:
Diese Funktion prüft, ob es noch offene Stellen gibt, bevor sie mit der Entscheidungsfindung fortfährt. Gibt es keine offenen Positionen, wird der aktuelle RSI-Wert abgerufen, und es werden sowohl kurzfristige als auch langfristige gleitende Durchschnitte berechnet, um den Markttrend zu bestimmen. Wenn sich der Markt in einem Aufwärtstrend befindet und der RSI überverkaufte Bedingungen anzeigt, kann er einen Kaufauftrag erteilen. Befindet sich der Markt dagegen in einem Abwärtstrend und signalisiert der RSI überkaufte Bedingungen, kann er einen Verkaufsauftrag erteilen. Wenn es offene Positionen gibt, verwaltet die Funktion diese mit einem Trailing-Stop-Mechanismus.
//+------------------------------------------------------------------+ //| Check and execute Trend Constraint EA trading logic | //+------------------------------------------------------------------+ void CheckTrendConstraintTrading() { // Check if there are any positions open if (PositionsTotal() == 0) { // Get RSI value double rsi_value; double rsi_values[]; if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0) { Print("Failed to get RSI value"); return; } rsi_value = rsi_values[0]; // Calculate moving averages double ma_short = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE); double ma_long = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE); // Determine trend direction bool is_uptrend = ma_short > ma_long; bool is_downtrend = ma_short < ma_long; // Check for buy conditions if (is_uptrend && rsi_value < RSI_Oversold) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); double stopLossPrice = currentPrice - StopLoss * _Point; double takeProfitPrice = currentPrice + TakeProfit * _Point; // Attempt to open a Buy order if (trade.Buy(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Buy") > 0) { Print("Trend Following Buy order placed."); } else { Print("Error placing Trend Following Buy order: ", GetLastError()); } } // Check for sell conditions else if (is_downtrend && rsi_value > RSI_Overbought) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); double stopLossPrice = currentPrice + StopLoss * _Point; double takeProfitPrice = currentPrice - TakeProfit * _Point; // Attempt to open a Sell order if (trade.Sell(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Sell") > 0) { Print("Trend Following Sell order placed."); } else { Print("Error placing Trend Following Sell order: ", GetLastError()); } } } else { // Implement Trailing Stop for open positions TrailingStopLogic(); } }
Die Funktion der Breakout-Strategie
Diese Funktion wurde entwickelt, um Ausbruchsbedingungen auf der Grundlage des Donchian Channel Indikators zu bewerten. Es passt die Größe der erforderlichen Puffer an, um die neuesten Daten zu erfassen, und prüft auf potenzielle Ausbruchsmöglichkeiten. Die Strategie sucht nach dem Überschreiten bestimmter Kursniveaus, die auf eine deutliche Kursbewegung in eine Richtung hindeuten könnten. Wenn diese Bedingungen erfüllt sind, führt der EA entsprechende Aufträge aus.
/+-------------------------------------------------------------------+ //| Check and execute Breakout EA trading logic | //+------------------------------------------------------------------+ void CheckBreakoutTrading() { // Resize buffers to get the latest data ArrayResize(ExtUpBuffer, 2); ArrayResize(ExtDnBuffer, 2); // Get the latest values from the Donchian Channel if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0) { Print("Error reading Donchian Channel buffer. Error: ", GetLastError()); return; } // Get the close price of the current candle double closePrice = iClose(Symbol(), Period(), 0); // Get the daily open and close for the previous day double lastOpen = iOpen(Symbol(), PERIOD_D1, 1); double lastClose = iClose(Symbol(), PERIOD_D1, 1); // Determine if the last day was bullish or bearish bool isBullishDay = lastClose > lastOpen; // Bullish if close > open bool isBearishDay = lastClose < lastOpen; // Bearish if close < open // Check if there are any open positions before executing breakout strategy if (PositionsTotal() == 0) // Only proceed if no positions are open { // Buy condition: Closing price is above the upper Donchian band on a bullish day if (closePrice > ExtUpBuffer[1] && isBullishDay) { double stopLoss = closePrice - pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice + pipsToTakeProfit * _Point; // Calculate take profit OpenBreakoutBuyOrder(stopLoss, takeProfit); } // Sell condition: Closing price is below the lower Donchian band on a bearish day if (closePrice < ExtDnBuffer[1] && isBearishDay) { double stopLoss = closePrice + pipsToStopLoss * _Point; // Calculate stop loss double takeProfit = closePrice - pipsToTakeProfit * _Point; // Calculate take profit OpenBreakoutSellOrder(stopLoss, takeProfit); } } }
Prüfen des Breakout-Handels:
Diese Funktion ruft die letzten Werte des Donchian Channel Indikators ab, um die Marktbedingungen zu analysieren. Sie ermittelt den Schlusskurs der aktuellen Kerze und die täglichen Eröffnungs- und Schlusskurse des Vortages. Anhand des Vergleichs dieser Kurse wird ermittelt, ob der Vortag stieg oder fiel war. Die Funktion prüft dann, ob offene Positionen vorhanden sind, bevor sie die Ausbruchsstrategie ausführt. Wenn es keine offenen Positionen gibt, wird geprüft, ob die Bedingungen für die Eröffnung eines Kaufauftrags (wenn der Schlusskurs an einem Aufwärtstag über dem oberen Band liegt) oder eines Verkaufsauftrags (wenn der Schlusskurs an einem Abwärtstag unter dem unteren Band liegt) gegeben sind. Es berechnet die Stop-Loss und Take-Profit für jeden Handel, bevor es versucht, den Auftrag zu platzieren.
//+------------------------------------------------------------------+ //| Open a buy order for the Breakout strategy | //+------------------------------------------------------------------+ void OpenBreakoutBuyOrder(double stopLoss, double takeProfit) { if (trade.Buy(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Buy")) { Print("Breakout Buy order placed."); } else { Print("Error placing Breakout Buy order: ", GetLastError()); } } //+------------------------------------------------------------------+ //| Open a sell order for the Breakout strategy | //+------------------------------------------------------------------+ void OpenBreakoutSellOrder(double stopLoss, double takeProfit) { if (trade.Sell(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Sell")) { Print("Breakout Sell order placed."); } else { Print("Error placing Breakout Sell order: ", GetLastError()); } }
4. Ablaufzeit der Positionen prüfen
Die Funktion CheckOrderExpiration überprüft alle offenen Positionen, um diejenigen zu identifizieren und zu schließen, die eine bestimmte Laufzeit überschritten haben. Diese Funktion ist von entscheidender Bedeutung für die Aufrechterhaltung eines frischen Handelsumfelds, ein effektives Risikomanagement und die Verhinderung, dass alte Positionen länger als strategisch ratsam offen bleiben. Die Funktion prüft die magische Zahl jeder Position, um festzustellen, ob sie Teil der Trendfolgestrategie ist, und vergleicht die aktuelle Zeit mit der offenen Zeit der Position, um festzustellen, ob sie geschlossen werden sollte.
//+------------------------------------------------------------------+ //| Check for expired Trend Following orders | //+------------------------------------------------------------------+ void CheckOrderExpiration() { for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if (PositionSelectByTicket(ticket)) { long magicNumber = PositionGetInteger(POSITION_MAGIC); // Check if it's a Trend Following position if (magicNumber == MagicNumber) { datetime openTime = (datetime)PositionGetInteger(POSITION_TIME); if (TimeCurrent() - openTime >= OrderLifetime) { // Attempt to close the position if (trade.PositionClose(ticket)) { Print("Trend Following position closed due to expiration."); } else { Print("Error closing position due to expiration: ", GetLastError()); } } } } } }
5. Logik des nachlaufenden Stopps
Die Methode TrailingStopLogic ist für die Verwaltung bestehender offener Positionen zuständig, indem sie deren Regeln für Stop-Loss gemäß den Trailing-Stop anpasst. Bei Kaufpositionen wird der Stop-Loss nach oben verschoben, wenn der aktuelle Kurs die Trailing-Stop-Schwelle überschreitet. Bei Verkaufspositionen senkt er den Stop-Loss ab, wenn die Bedingungen erfüllt sind. Dieser Ansatz trägt dazu bei, Gewinne zu sichern, da der Stop-Loss günstigen Kursbewegungen folgen kann, wodurch das Verlustrisiko bei einer Marktumkehr verringert wird.
//+------------------------------------------------------------------+ //| Manage trailing stops for open positions | //+------------------------------------------------------------------+ void TrailingStopLogic() { for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if (PositionSelectByTicket(ticket)) { double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); double stopLoss = PositionGetDouble(POSITION_SL); // Update stop loss for long positions if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { if (currentPrice - stopLoss > TrailingStop * _Point || stopLoss == 0) { trade.PositionModify(ticket, currentPrice - TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } // Update stop loss for short positions else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { if (stopLoss - currentPrice > TrailingStop * _Point || stopLoss == 0) { trade.PositionModify(ticket, currentPrice + TrailingStop * _Point, PositionGetDouble(POSITION_TP)); } } } } }
6. Bereinigungsfunktion
Die Funktion OnDeinit() dient als Bereinigungsroutine, die ausgeführt wird, wenn der Expert Advisor (EA) aus dem Chart entfernt wird. Diese Funktion kümmert sich um die Freigabe aller zugewiesenen Ressourcen oder Indikator-Handles und stellt sicher, dass es keine Speicherlecks oder „Dangling References“ gibt. Er bestätigt, dass der EA ordnungsgemäß deinitialisiert wurde und protokolliert diese Aktion.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Release indicators and handles IndicatorRelease(rsi_handle); IndicatorRelease(handle); Print("Expert deinitialized."); }
Tests und Ergebnisse
Hier ist unsere Strategie-Tester Erfahrung in das Bild unten mit neuen Funktionen funktionieren.
Trend Constraint Expert: Donchian Channel Breakout und Trendfolgestrategien
Schlussfolgerung
In unserer Diskussion ging es um die Herausforderung, sich an unterschiedliche Marktbedingungen anzupassen, indem mehrere Strategien in einen einzigen Expert Advisor (EA) integriert werden. Wir haben zunächst einen Mini-Breakout-EA entwickelt, um den Breakout-Prozess effektiv zu verwalten, bevor wir ihn in unseren Haupt-Experten Trend Constraint integriert haben. Diese Integration verbesserte die Funktionalität des EA, indem sie die Ausbruchsstrategie mit der Marktstimmung auf einem höheren Zeitrahmen, insbesondere der D1-Candlestick-Analyse, in Einklang brachte, was übermäßige Handelsausführungen reduzierte.
Jeder im Strategietester ausgeführte Handel wurde klar mit der verwendeten Strategie kommentiert, was für Transparenz und Klarheit beim Verständnis der zugrunde liegenden Mechanismen sorgt. Ich kann sagen, dass man ein komplexes Problem effektiv angehen kann, wenn man es in kleinere, überschaubare Komponenten aufteilt und jede einzelne Schritt für Schritt angeht.
Unsere Umsetzung zeigt zwar Potenzial, ist aber noch verbesserungswürdig. Der EA dient als Lehrmittel und zeigt, wie verschiedene Strategien zusammenwirken können, um bessere Handelsergebnisse zu erzielen. Ich ermutige Sie, mit den bereitgestellten EAs zu experimentieren und sie an Ihre Handelsstrategien anzupassen. Ihr Feedback ist wichtig, da wir gemeinsam die Möglichkeiten der Kombination mehrerer Strategien im algorithmischen Handel untersuchen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16137





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