English Русский 中文 Español 日本語
preview
Wie man die automatische Optimierung in MQL5 Expert Advisors implementiert

Wie man die automatische Optimierung in MQL5 Expert Advisors implementiert

MetaTrader 5Beispiele | 15 November 2024, 10:44
365 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Machen Sie sich bereit, in die wunderbare Welt der automatisch optimierenden Forex-Handelsalgorithmen eingeführt zu werden. Sie kann es Ihrem Expert Advisor (EA) ermöglichen, sich für die nächste Iteration des Handels auf der Grundlage der Marktbedingungen nach einem abgeschlossenen Handel anzupassen.

Betrachten Sie Ihren EA als einen versierten Händler, der die Trends anhand von gleitenden Durchschnitten beobachtet. Es hat funktioniert, aber was wäre, wenn es eine marktbewusste Blackbox wäre, die mit der Zeit noch lernen könnte, ihre Strategie zu optimieren? Dies ist der Prozess der automatischen Optimierung.

Einer der Hauptvorteile eines Expert Advisors (EA) besteht darin, dass er sich an die Marktbedingungen anpassen kann, wenn diese sich weiter verändern. Der EA passt sich automatisch an das aktuelle Marktumfeld an und reduziert so die ständige manuelle Überwachung und Änderung der Parameter. Dies ermöglicht es den Händlern, kurzfristige, sekundengenaue Chancen konsequent zu nutzen und ihre Handelsstrategie ohne Unterbrechungen auszuführen. Außerdem ist der EA in der Lage, Handelsstrategien jeden Tag und die ganze Zeit über zu optimieren.

Allerdings gibt es einige Fallstricke, die man beachten sollte. Eine Herausforderung ist das Risiko der Überanpassung an aktuelle Daten, was zu einer schlechten Leistung unter verschiedenen Marktbedingungen führen kann. Eine effiziente Verwaltung der Rechenressourcen ist ebenfalls von entscheidender Bedeutung, da die Komplexität des Codes bei der Automatisierung von Strategien zunehmen kann. Es kann schwierig sein, die Stabilität bei Parameteränderungen aufrechtzuerhalten, und die Zuordnung der Leistung kann manchmal zu einem Problem werden.

In diesem Leitfaden werden wir den Prozess der Erstellung eines automatisch optimierenden EA erkunden, einschließlich der Automatisierung von Strategien mit nutzerdefinierten Indikatoren. Wir werden eine robuste Optimierungslogik, bewährte Verfahren für die Parameterauswahl und die Rekonstruktion von Strategien mit Backtesting behandeln. Darüber hinaus werden übergeordnete Methoden wie die Walk-Forward-Optimierung erörtert, um Ihren Handelsansatz zu verbessern.

Wie Sie Ihre Ziele erreichen und produktiv sein können (und wie Sie nicht in Fallen tappen!)

Wir werden unseren Handelsplan auf eine Crossover-Strategie mit gleitendem Durchschnitt stützen, die die grundlegendste aller Strategien ist, aber in trendorientierten Märkten nie versagt.


1. Einrichten von Bibliotheken, Eingabeparametern und Optimierungsbereichen für die Auto-Optimierung von EA

1.1 Importieren der erforderlicher Bibliotheken

In der ersten Zeile durch Einbindung der notwendigen MQL5-Bibliotheken:

#include <Trade\Trade.mqh>
#include <Arrays\ArrayObj.mqh>

Die Trade-Bibliothek bietet Funktionen zur Ausführung von Trades, während die ArrayObj-Bibliothek die Arbeit mit dynamischen Arrays von Objekten ermöglicht, die wir zum Speichern von Optimierungsergebnissen verwenden werden.

1.2 Definition der Eingabeparameter

Als Nächstes definieren wir die Eingabeparameter für unseren EA:

input int MA_Fast_Period = 10;     // Fast Moving Average Period
input int MA_Slow_Period = 20;     // Slow Moving Average Period
input ENUM_MA_METHOD MA_Method = MODE_SMA; // Moving Average Method
input ENUM_APPLIED_PRICE Applied_Price = PRICE_CLOSE; // Applied Price
input double LotSize = 0.1;        // Lot Size
input int StopLoss = 50;           // Stop Loss in points
input int TakeProfit = 100;        // Take Profit in points

// Optimization parameters
input bool AutoOptimize = false;   // Enable Auto Optimization
input int OptimizationPeriod = 5000; // Number of ticks between optimizations
input int MinDataPoints = 1000;    // Minimum number of data points for optimization

Diese Eingabeparameter ermöglichen es dem Nutzer, das Verhalten und die Optimierungseinstellungen des EA direkt über die MetaTrader-Oberfläche zu konfigurieren.

1.3 Globale Variablen und Handles

Anschließend deklarieren wir globale Variablen und Handles, die im gesamten EA verwendet werden:

CTrade trade;
int fastMA_Handle, slowMA_Handle;
double fastMA[], slowMA[];
int tickCount = 0;
CArrayObj* optimizationResults;

// Optimization ranges
const int MA_Fast_Min = 5, MA_Fast_Max = 50, MA_Fast_Step = 1;
const int MA_Slow_Min = 10, MA_Slow_Max = 100, MA_Slow_Step = 1;

Das Objekt „CTrade“ verwaltet die Handelsoperationen, während „fastMA_Handle“ und „slowMA_Handle“ für die Verwaltung der Indikatoren des gleitenden Durchschnitts verwendet werden. Das Array „optimizationResults“ speichert die Ergebnisse unserer Optimierungstests.

1.4 Optimierungseinstellungen

Die Optimierungseinstellungen legen den Wertebereich fest, den wir für jeden Parameter testen:

  • Schnelle MA-Periodenlänge: Von 5 bis 50, gestaffelt nach 1- Slow MA Period: Von 10 bis 100, schrittweise um 1
Diese Bereiche können je nach Ihren spezifischen Anforderungen und den Merkmalen des von Ihnen gehandelten Instruments angepasst werden.


2. Implementierung der zentralen Handelslogik

Nachdem wir unsere EA-Struktur eingerichtet haben, können wir nun die Kernfunktionen implementieren, die die Initialisierung, Deinitialisierung und Tick-Verarbeitung übernehmen.

2.1 Die Funktion OnInit()

Die Funktion „OnInit()“ wird aufgerufen, wenn der EA zum ersten Mal in einen Chart geladen wird. So setzen wir es um:

int OnInit()
{
    // Initialize MA handles
    fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
    slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);
    
    if(fastMA_Handle == INVALID_HANDLE || slowMA_Handle == INVALID_HANDLE)
    {
        Print("Failed to create MA indicators");
        return INIT_FAILED;
    }
    
    // Initialize optimization results array
    optimizationResults = new CArrayObj();
    
    return INIT_SUCCEEDED;
}

Diese Funktion erstellt die Indikatoren des gleitenden Durchschnitts und initialisiert das Array der Optimierungsergebnisse. Wenn die Indikatoren nicht initialisiert werden können, wird der EA nicht gestartet.

2.2 Die Funktion OnDeinit()

Die Funktion „OnDeinit()“ wird aufgerufen, wenn der EA aus dem Chart entfernt oder das Terminal geschlossen wird:

void OnDeinit(const int reason)
{
    // Release MA handles
    IndicatorRelease(fastMA_Handle);
    IndicatorRelease(slowMA_Handle);
    
    // Clean up optimization results
    if(optimizationResults != NULL)
    {
        delete optimizationResults;
        optimizationResults = NULL;
    }
}

Diese Funktion stellt sicher, dass die Indikator-Handles ordnungsgemäß freigegeben werden und der vom Array der Optimierungsergebnisse belegte Speicher freigegeben wird.

2.3 Die Funktion OnTick()

Die Funktion „OnTick()“ ist das Herzstück unseres EA und wird bei jedem Tick des ausgewählten Symbols aufgerufen:

void OnTick()
{
    // Check if we have enough bars to calculate MAs
    if(Bars(_Symbol, PERIOD_CURRENT) < MA_Slow_Period) return;
    
    // Copy MA values
    if(CopyBuffer(fastMA_Handle, 0, 0, 2, fastMA) != 2) return;
    if(CopyBuffer(slowMA_Handle, 0, 0, 2, slowMA) != 2) return;
    
    // Auto Optimization
    if(AutoOptimize && ++tickCount >= OptimizationPeriod)
    {
        Optimize();
        tickCount = 0;
    }
    
    // Trading logic
    if(fastMA[1] <= slowMA[1] && fastMA[0] > slowMA[0])
    {
        // Open buy position
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        trade.Buy(LotSize, _Symbol, ask, ask - StopLoss * _Point, ask + TakeProfit * _Point);
    }
    else if(fastMA[1] >= slowMA[1] && fastMA[0] < slowMA[0])
    {
        // Open sell position
        double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        trade.Sell(LotSize, _Symbol, bid, bid + StopLoss * _Point, bid - TakeProfit * _Point);
    }
}

Diese Funktion erfüllt mehrere wichtige Aufgaben:

  1. Es wird geprüft, ob genügend historische Daten für die Berechnung der gleitenden Durchschnitte vorhanden sind.
  2. Sie ruft die aktuellen Werte der gleitenden Durchschnitte ab.
  3. Wenn die automatische Optimierung aktiviert ist und es an der Zeit ist, zu optimieren (basierend auf „tickCount“), wird die Funktion „Optimize()“ aufgerufen.
  4. Es implementiert die Handelslogik und eröffnet Kauf- oder Verkaufspositionen auf der Grundlage von Überkreuzungen gleitender Durchschnitte.



3. Implementierung der Optimierungslogik

Der Kern unserer automatischen Optimierungsfunktion liegt in der Funktion „Optimize()“. Schauen wir uns die einzelnen Bestandteile an.

3.1 Die Funktion Optimieren()

Hier ist die allgemeine Struktur der Funktion „Optimize()“:

void Optimize()
{
    Print("Starting optimization...");
    
    optimizationResults.Clear();
    
    // Loop through all combinations of MA periods
    for(int fastPeriod = MA_Fast_Min; fastPeriod <= MA_Fast_Max; fastPeriod += MA_Fast_Step)
    {
        for(int slowPeriod = MA_Slow_Min; slowPeriod <= MA_Slow_Max; slowPeriod += MA_Slow_Step)
        {
            if(slowPeriod <= fastPeriod) continue; // Slow period should be greater than fast period
            
            double profit = TestParameters(fastPeriod, slowPeriod);
            
            OptimizationResult* result = new OptimizationResult;
            result.fastPeriod = fastPeriod;
            result.slowPeriod = slowPeriod;
            result.profit = profit;
            
            optimizationResults.Add(result);
        }
    }
    
    // Find the best result
    OptimizationResult* bestResult = NULL;
    for(int i = 0; i < optimizationResults.Total(); i++)
    {
        OptimizationResult* currentResult = optimizationResults.At(i);
        if(bestResult == NULL || currentResult.profit > bestResult.profit)
        {
            bestResult = currentResult;
        }
    }
    
    if(bestResult != NULL)
    {
// Update the EA parameters
        MA_Fast_Period = bestResult.fastPeriod;
        MA_Slow_Period = bestResult.slowPeriod;
        
        // Update indicator handles
        IndicatorRelease(fastMA_Handle);
        IndicatorRelease(slowMA_Handle);
        fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
        slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);
        
        Print("Optimization complete. New parameters: Fast MA = ", MA_Fast_Period, ", Slow MA = ", MA_Slow_Period);
    }
    else
    {
        Print("Optimization failed to find better parameters.");
    }
}

3.2 Schleife durch Parameterkombinationen

Die verschachtelten for-Schleifen in der Funktion „Optimize()“ ermöglichen es uns, alle Kombinationen von schnellen und langsamen gleitenden Durchschnittsperioden innerhalb der von uns festgelegten Bereiche zu testen. Dies ist ein sogenannter „Brute-Force“-Ansatz zur Optimierung.

for(int fastPeriod = MA_Fast_Min; fastPeriod <= MA_Fast_Max; fastPeriod += MA_Fast_Step)
{
    for(int slowPeriod = MA_Slow_Min; slowPeriod <= MA_Slow_Max; slowPeriod += MA_Slow_Step)
    {
        if(slowPeriod <= fastPeriod) continue; // Slow period should be greater than fast period
        
        double profit = TestParameters(fastPeriod, slowPeriod);
        
        // Store results...
    }
}

Wir lassen Kombinationen aus, bei denen die langsame Periodenlänge kleiner oder gleich der schnellen Periodenlänge ist, da dies für unsere Strategie keinen Sinn machen würde.

3.3 Speichern und Vergleichen von Ergebnissen

Für jede gültige Kombination rufen wir „TestParameter()“ auf, um ihre Leistung zu bewerten. Die Ergebnisse werden in dem Objekt „OptimizationResult“ gespeichert und zu unserem Array „OptimizationResults“ hinzugefügt.

Nachdem wir alle Kombinationen getestet haben, gehen wir die Ergebnisse in einer Schleife durch, um den besten Parametersatz zu finden:

OptimizationResult* bestResult = NULL;
for(int i = 0; i < optimizationResults.Total(); i++)
{
    OptimizationResult* currentResult = optimizationResults.At(i);
    if(bestResult == NULL || currentResult.profit > bestResult.profit)
    {
        bestResult = currentResult;
    }
}

Wenn ein optimales Ergebnis gefunden wird, aktualisieren wir die Parameter des EA und erstellen die Handles der Indikatoren mit den neuen Perioden neu.


4. Testen der Parameter

Die Funktion „TestParameters()“ ist entscheidend für die Auswertung der einzelnen Parametersätze. Schauen wir uns das im Detail an.

4.1 Die Funktion TestParameter()

double TestParameters(int fastPeriod, int slowPeriod)
{
    int maFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MA_Method, Applied_Price);
    int maSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MA_Method, Applied_Price);
    
    if(maFast == INVALID_HANDLE || maSlow == INVALID_HANDLE)
    {
        Print("Failed to create MA indicators for testing");
        return -DBL_MAX;
    }
    
    double fastBuffer[], slowBuffer[];
    ArraySetAsSeries(fastBuffer, true);
    ArraySetAsSeries(slowBuffer, true);
    
    int copied = CopyBuffer(maFast, 0, 0, MinDataPoints, fastBuffer);
    copied = MathMin(copied, CopyBuffer(maSlow, 0, 0, MinDataPoints, slowBuffer));
    
    if(copied < MinDataPoints)
    {
        Print("Not enough data for testing");
        return -DBL_MAX;
    }
    
    double profit = 0;
    for(int i = 1; i < copied; i++)
    {
        if(fastBuffer[i] > slowBuffer[i] && fastBuffer[i-1] <= slowBuffer[i-1])
        {
            // Buy signal
            profit += Close[i-1] - Open[i];
        }
        else if(fastBuffer[i] < slowBuffer[i] && fastBuffer[i-1] >= slowBuffer[i-1])
        {
            // Sell signal
            profit += Open[i] - Close[i-1];
        }
    }
    
    IndicatorRelease(maFast);
    IndicatorRelease(maSlow);
    
    return profit;
}

4.2 Temporäre Indikatoren erstellen

Für jeden getesteten Parametersatz werden temporäre Indikatoren des gleitenden Durchschnitts erstellt:

int maFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MA_Method, Applied_Price);
int maSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MA_Method, Applied_Price);

Diese temporären Indikatoren ermöglichen es uns, gleitende Durchschnitte mit unterschiedlichen Zeiträumen zu berechnen, ohne unsere Haupt-Handelslogik zu beeinträchtigen.

4.3 Simulieren von Handelsgeschäften

Anschließend durchlaufen wir die historischen Daten und simulieren den Handel auf der Grundlage unserer Crossover-Logik der gleitenden Durchschnitte:

for(int i = 1; i < copied; i++)
{
    if(fastBuffer[i] > slowBuffer[i] && fastBuffer[i-1] <= slowBuffer[i-1])
    {
        // Buy signal
        profit += Close[i-1] - Open[i];
    }
    else if(fastBuffer[i] < slowBuffer[i] && fastBuffer[i-1] >= slowBuffer[i-1])
    {
        // Sell signal
        profit += Open[i] - Close[i-1];
    }
}

Diese vereinfachte Simulation geht davon aus, dass wir einen Handel zum Eröffnungskurs des auf einen Crossover folgenden Balkens eröffnen und zum Schlusskurs desselben Balkens schließen können.

4.4 Berechnung des Gewinns

Die Funktion gibt den Gesamtgewinn aus den simulierten Handelsgeschäfte zurück. Bei einer anspruchsvolleren Umsetzung könnten Sie andere Faktoren wie den maximalen Drawdown, die Sharpe Ratio oder die Gewinnrate berücksichtigen.


5. Optimierte Parameter anwenden

Sobald wir die leistungsfähigsten Parameter gefunden haben, soll sie unser EA verwenden.

5.1 EA-Parameter aktualisieren

Wir aktualisieren unsere globalen Variablen mit den neuen optimalen Werten:

MA_Fast_Period = bestResult.fastPeriod;
MA_Slow_Period = bestResult.slowPeriod;

5.2 Wiederherstellen von Indikator-Handles

Nachdem wir die Parameter aktualisiert haben, müssen wir unsere Indikator-Handles neu erstellen:

IndicatorRelease(fastMA_Handle);
IndicatorRelease(slowMA_Handle);
fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);

Dadurch wird sichergestellt, dass unsere Haupthandelslogik in Zukunft die neu optimierten Parameter verwenden wird.


6. Fortgeschrittene Optimierungstechniken

Während unsere aktuelle Implementierung eine solide Grundlage für die automatische Optimierung bietet, gibt es mehrere fortgeschrittene Techniken, die Sie in Betracht ziehen können, um die Leistung Ihres EA weiter zu verbessern.

6.1 Optimierung mehrerer Kriterien

Anstatt nur auf den Gewinn zu optimieren, können Sie mehrere Kriterien in Ihren Optimierungsprozess einbeziehen. Zum Beispiel:

struct OptimizationResult
{
    int fastPeriod;
    int slowPeriod;
    double profit;
    double drawdown;
    double sharpeRatio;
};

double CalculateScore(const OptimizationResult &result)
{
    return result.profit * 0.5 + result.sharpeRatio * 0.3 - result.drawdown * 0.2;
}

Mit diesem Ansatz können Sie mehrere Leistungsaspekte abwägen, was zu robusteren Parametersätzen führen kann.

6.2 Walk-Forward-Optimierung

Bei der Walk-Forward-Optimierung werden Ihre historischen Daten in mehrere Segmente unterteilt, die Optimierung wird für ein Segment durchgeführt und dann für das nächste getestet.

Dies kann dazu beitragen, eine Überanpassung zu verhindern:

void WalkForwardOptimization()
{
    int totalBars = Bars(_Symbol, PERIOD_CURRENT);
    int segmentSize = 1000; // Adjust as needed
    
    for(int i = 0; i < totalBars - 2*segmentSize; i += segmentSize)
    {
        // Optimize on segment i to i+segmentSize
        OptimizeSegment(i, i+segmentSize);
        
        // Test on segment i+segmentSize to i+2*segmentSize
        TestSegment(i+segmentSize, i+2*segmentSize);
    }
}

6.3 Adaptive Parameteranpassung

Anstatt die Parameter während der Optimierung vollständig zu ersetzen, können Sie ein System implementieren, das die Parameter schrittweise auf der Grundlage der jüngsten Leistung anpasst:

void AdaptParameters()
{
    double recentPerformance = CalculateRecentPerformance();
    double adaptationRate = 0.1; // Adjust as needed
    
    MA_Fast_Period += (int)((bestResult.fastPeriod - MA_Fast_Period) * adaptationRate * recentPerformance);
    MA_Slow_Period += (int)((bestResult.slowPeriod - MA_Slow_Period) * adaptationRate * recentPerformance);
}

Dieser Ansatz kann einen sanfteren Übergang zwischen Parametersätzen ermöglichen und die Auswirkungen kurzfristiger Marktstörungen auf Ihren Optimierungsprozess verringern.



7. Bewährte Praktiken und Überlegungen

Beachten Sie bei der Implementierung und Verfeinerung Ihres EA mit automatischer Optimierung diese bewährten Verfahren:

7.1 Auswahl der Optimierungsfrequenz

Die Häufigkeit der Optimierung kann die Leistung Ihres EAs erheblich beeinflussen. Eine zu häufige Optimierung kann zu einer Überreaktion auf kurzfristige Marktschwankungen führen, während eine zu seltene Optimierung zu verpassten Chancen führen kann.

Ziehen Sie die Implementierung einer dynamischen Optimierungsfrequenz in Betracht, die auf der Marktvolatilität oder der jüngsten Performance des EAs basiert:

bool ShouldOptimize()
{
    double recentVolatility = CalculateRecentVolatility();
    int dynamicPeriod = (int)(OptimizationPeriod * (1 + recentVolatility));
    
    return tickCount >= dynamicPeriod;
}

7.2 Ausgleich zwischen Anpassungsfähigkeit und Stabilität

Obwohl die Anpassungsfähigkeit ein entscheidender Vorteil von automatisch optimierten EAs ist, ist es wichtig, ein gewisses Maß an Stabilität zu wahren. Drastische Parameteränderungen können zu inkonsistentem Handelsverhalten führen.

Ziehen Sie in Erwägung, die Anzahl der Parameter, die bei einer einzelnen Optimierung geändert werden können, zu begrenzen:

void LimitParameterChange(int &parameter, int newValue, int maxChange)
{
    int change = newValue - parameter;
    change = MathMax(-maxChange, MathMin(change, maxChange));
    parameter += change;
}

7.3 Eine Überanpassung ist ein großes Risiko in jedem Optimierungsprozess. Ein überangepasster EA kann bei historischen Daten außergewöhnlich gut abschneiden, aber versagen, wenn er mit neuen Marktbedingungen konfrontiert wird. Um dieses Risiko zu mindern, ist es wichtig, eine ausreichende Menge an historischen Daten für die Optimierung zu verwenden. Darüber hinaus kann die Durchführung von Out-of-Sample-Tests oder Walk-Forward-Optimierung dazu beitragen, die Robustheit sicherzustellen. Die Komplexität Ihrer Strategie sollte auch im Verhältnis zur Menge der verfügbaren Daten betrachtet werden. Eine genaue Überwachung der Live-Performance ist von entscheidender Bedeutung, und Sie müssen darauf vorbereitet sein, einzugreifen, wenn das Verhalten des EA erheblich von den Backtest-Ergebnissen abweicht.

7.4 Die automatische Optimierung kann sehr rechenintensiv sein, weshalb die Gewährleistung der Recheneffizienz von entscheidender Bedeutung ist. Um Ihren EA reaktionsfähig zu halten, ist es ratsam, Optimierungen außerhalb der Geschäftszeiten oder in einem separaten Thread durchzuführen. Effiziente Datenstrukturen und Algorithmen können die Verarbeitungszeit erheblich verkürzen. Für intensivere Optimierungsaufgaben sind Cloud-basierte Lösungen möglicherweise eine gute Option.


8. Die Entwicklung eines automatisch optimierenden EA bringt neue Herausforderungen bei der Fehlersuche und -behebung mit sich

8.1 Während der Entwicklung können einige allgemeine Probleme auftreten. Wenn Ihr EA beispielsweise bei jedem Durchlauf inkonsistente Ergebnisse liefert, sollten Sie sicherstellen, dass Sie konsistente Daten verwenden und Ihren Code auf Zufallselemente überprüfen. Eine schlechte Live-Performance trotz guter Backtest-Ergebnisse könnte auf Faktoren wie Slippage, Spread oder veränderte Marktbedingungen zurückzuführen sein. Wenn die Optimierung fehlschlägt oder unerwartete Ergebnisse liefert, ist es wichtig, Ihre Optimierungskriterien zu überprüfen und sicherzustellen, dass Ihre Funktion TestParameters() wie erwartet funktioniert.

8.2 Um die Fehlersuche zu erleichtern, wird dringend empfohlen, eine umfassende Protokollierung zu implementieren. Auf diese Weise können Sie das Verhalten des EA und den Optimierungsprozess im Detail verfolgen und so Probleme besser erkennen und beheben.

Implementieren Sie eine umfassende Protokollierung, um das Verhalten des EA und den Optimierungsprozess zu verfolgen:
void Log(string message)
{
    Print(TimeToString(TimeCurrent()) + ": " + message);
    
    int handle = FileOpen("EA_Log.txt", FILE_WRITE|FILE_READ|FILE_TXT);
    if(handle != INVALID_HANDLE)
    {
        FileSeek(handle, 0, SEEK_END);
        FileWriteString(handle, TimeToString(TimeCurrent()) + ": " + message + "\n");
        FileClose(handle);
    }
}

Verwenden Sie diese Funktion, um wichtige Ereignisse, Parameteränderungen und alle Fehler, die während des Betriebs oder der Optimierung auftreten, zu protokollieren.

8.3 Der MetaTrader Strategy Tester ist ein unschätzbares Werkzeug für die Fehlersuche in Ihrem EA. Sie können den visuellen Modus verwenden, um das Verhalten Ihres EA Takt für Takt durchzugehen, was einen detaillierten Einblick in die Funktionsweise des EA in Echtzeit bietet. Darüber hinaus kann ein Vergleich der Optimierungsergebnisse im Strategy Tester mit denen der automatischen Optimierung Ihres EAs helfen, etwaige Diskrepanzen zu erkennen. Die Optimierungsfunktion des Strategy Testers ist auch eine nützliche Methode, um die Funktionalität Ihrer TestParameters()-Funktion zu überprüfen.



9. Leistung und Herausforderungen des automatisch optimierenden Moving Average EA

9.1 Um die potenziellen Vorteile und Herausforderungen der automatischen Optimierung zu veranschaulichen, betrachten wir eine hypothetische Fallstudie.

Wir haben unseren automatisch optimierenden Moving Average Crossover EA auf EURUSD H1-Daten von 2010 bis 2020 getestet. Der EA wurde so konfiguriert, dass er seine Parameter alle 5000 Ticks optimiert. Die Ergebnisse waren vielversprechend und zeigten einen Gesamtnettogewinn von 15.420 $, einen Gewinnfaktor von 1,65 und einen maximalen Drawdown von 2.105 $ bei insgesamt 1.247 Trades.

Anschließend haben wir diese Ergebnisse mit demselben EA mit statischen Parametern (MA_Fast_Period = 10, MA_Slow_Period = 20) verglichen. Die statische Version ergab einen Gesamtnettogewinn von 8.750 $, einen Gewinnfaktor von 1,38 und einen höheren maximalen Drawdown von 3.210 $ bei 1.562 Gesamtgeschäften. Dieser Vergleich zeigte, dass die Version mit automatischer Optimierung sowohl die Gesamtrentabilität als auch die risikobereinigten Renditen deutlich verbesserte.

9.2 Die Ergebnisse der Backtests sind zwar vielversprechend, doch sind bei der Bewertung der realen Leistung mehrere wichtige Faktoren zu berücksichtigen. Änderungen des Marktregimes, wie die Finanzkrise 2008 oder die COVID-19-Pandemie 2020, könnten die Anpassungsfähigkeit des EA beeinträchtigen, und dies muss sorgfältig bewertet werden. Darüber hinaus sollten die Transaktionskosten berücksichtigt werden, um sicherzustellen, dass die verbesserte Performance eine erhöhte Handelstätigkeit rechtfertigt. Die für eine kontinuierliche Optimierung in einer Live-Umgebung erforderlichen Rechenressourcen sind ein weiterer Aspekt, ebenso wie die psychologische Belastbarkeit, die erforderlich ist, um mit Perioden geringer Leistung fertig zu werden, während sich der EA an neue Bedingungen anpasst.


10. Zukünftige Trends bei der Auto-Optimierung von EAs: Maschinelles Lernen, externe Daten und Cloud-Lösungen

10.1 Mit der Weiterentwicklung der Handelstechnologie zeichnen sich spannende Entwicklungen für die automatische Optimierung von EAs ab. Eine solche Entwicklung ist die Integration von Algorithmen des maschinellen Lernens in den Devisenhandel, die den Optimierungsprozess durch die Erkennung komplexer Muster in den Marktdaten verbessern könnten. Dies eröffnet Möglichkeiten für noch anpassungsfähigere und effizientere Handelsstrategien in der Zukunft.

Algorithmen des maschinellen Lernens können den Optimierungsprozess potenziell verbessern, indem sie komplexe Muster in Marktdaten erkennen:

from sklearn.ensemble import RandomForestRegressor

def ml_optimize(data, labels):
    model = RandomForestRegressor(n_estimators=100)
    model.fit(data, labels)
    return model.feature_importances_

Während in diesem Beispiel Python verwendet wird, können ähnliche maschinelle Lerntechniken in MQL5 implementiert oder über externe Bibliotheken integriert werden.

10.2 Integration mit externen Datenquellen

Die Einbeziehung externer Daten (Wirtschaftsindikatoren, Stimmungsanalysen usw.) in Ihren Optimierungsprozess kann einen umfassenderen Blick auf die Marktbedingungen ermöglichen:

string GetExternalData()
{
    string cookie=NULL,headers;
    char post[],result[];
    int res;
    
    string url="https://api.example.com/economic-data";
    
    res=WebRequest("GET",url,cookie,NULL,500,post,0,result,headers);
    
    if(res==-1)
    {
        Print("Error in WebRequest. Error code  =",GetLastError());
        return "";
    }
    
    string resultString=CharArrayToString(result);
    return resultString;
}

Durch die Integration externer Daten kann Ihr EA potenziell fundiertere Entscheidungen darüber treffen, wann und wie er seine Parameter optimieren soll.

10.3 Cloud-basierte Optimierung

Da die Optimierungsaufgaben immer komplexer werden, bieten cloudbasierte Lösungen das Potenzial für leistungsfähigere und flexiblere Optimierungsprozesse:

void CloudOptimize()
{
    string optimizationData = PrepareOptimizationData();
    string url = "https://your-cloud-service.com/optimize";
    string headers = "Content-Type: application/json\r\n";
    char post[], result[];
    string resultHeaders;
    
    StringToCharArray(optimizationData, post);
    
    int res = WebRequest("POST", url, headers, 30000, post, result, resultHeaders);
    
    if(res == -1)
    {
        Print("Error in WebRequest. Error code =", GetLastError());
        return;
    }
    
    string optimizationResult = CharArrayToString(result);
    ApplyCloudOptimizationResult(optimizationResult);
}

Mit diesem Ansatz können Sie mehr Rechenleistung und potenziell anspruchsvollere Optimierungsalgorithmen nutzen, als dies auf einem lokalen Rechner möglich wäre.

Die Entwicklung eines effektiven, selbstoptimierenden EA ist ein fortlaufender Prozess, der kontinuierliche Verbesserung und Lernen erfordert. Eine Strategie zur Verbesserung ist die regelmäßige Überprüfung der Leistung der EA. Diese Praxis wird dazu beitragen, Bereiche mit Verbesserungsbedarf zu ermitteln und sicherzustellen, dass der EA auch unter veränderten Marktbedingungen optimal funktioniert.

void WeeklyPerformanceReview()
{
    datetime startOfWeek = iTime(_Symbol, PERIOD_W1, 0);
    double weeklyProfit = 0;
    int totalTrades = 0;
    
    for(int i = 0; i < HistoryDealsTotal(); i++)
    {
        ulong ticket = HistoryDealGetTicket(i);
        if(HistoryDealGetInteger(ticket, DEAL_TIME) >= startOfWeek)
        {
            weeklyProfit += HistoryDealGetDouble(ticket, DEAL_PROFIT);
            totalTrades++;
        }
    }
    
    Print("Weekly Performance: Profit = ", weeklyProfit, ", Trades = ", totalTrades);
}

Nutzen Sie diese Überprüfungen, um verbesserungswürdige Bereiche und potenzielle Probleme in Ihrem Optimierungsprozess zu ermitteln.

Es ist wichtig, über Handelstechnologie, Marktdynamik und Vorschriften auf dem Laufenden zu bleiben. Gründliche Tests, einschließlich Out-of-Sample- und Forward-Tests, sind von entscheidender Bedeutung. Kontinuierliches Lernen und Überwachung sind der Schlüssel zum langfristigen Erfolg. Die automatische Optimierung ist zwar leistungsstark, sollte aber solide Handelsgrundsätze und Risikomanagement ergänzen. Bleiben Sie neugierig und vorsichtig und achten Sie auf den Schutz Ihres Kapitals.

Viel Spaß beim Handeln!

Für diejenigen, die ihr Verständnis von automatischer Optimierung und algorithmischem Handel vertiefen möchten, gibt es hier einige zusätzliche Ressourcen:

  1. Building Reliable Trading Systems von Keith Fitschen
  2. Algorithmic Trading: Winning Strategies and Their Rationale von Ernie Chan
  3. Machine Learning for Algorithmic Trading von Stefan Jansen
  4. MQL5-Dokumentation: MQL5-Dokumentation
  5. Forex Factory Forum - Forum für Kodierer Forex Factory Forum
  6. Quantopian Lectures: Kurse zum Thema von Quantopian


11. Beispiel EA-Code

Nachfolgend finden Sie den vollständigen Code für den EA mit automatischer Optimierung. Kopieren Sie diesen Code in Ihren MetaEditor und kompilieren Sie ihn zur Verwendung in MetaTrader 5.

//+------------------------------------------------------------------+
//|                 Auto-Optimizing Moving Average Crossover EA      |
//| Copyright 2024, Javier Santiago Gaston de Iriarte Cabrera        |
//|               https://www.mql5.com/en/users/jsgaston/news        |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Javier Santiago Gaston de Iriarte Cabrera"
#property link      "https://www.mql5.com/en/users/jsgaston/news"
#property version   "1.00"
#property strict

// Include necessary libraries
#include <Trade\Trade.mqh>
#include <Arrays\ArrayObj.mqh>

// Input parameters
input ENUM_MA_METHOD MA_Method = MODE_SMA; // Moving Average Method
input ENUM_APPLIED_PRICE Applied_Price = PRICE_CLOSE; // Applied Price
input double LotSize = 0.01;        // Lot Size
input int StopLoss = 100;           // Stop Loss in points
input int TakeProfit = 200;        // Take Profit in points
input int Initial_MA_Fast_Period = 10;  // Initial Fast Moving Average Period
input int Initial_MA_Slow_Period = 20;  // Initial Slow Moving Average Period

// Optimization parameters
input bool AutoOptimize = true;    // Enable Auto Optimization
input int OptimizationPeriod = 5000; // Number of ticks between optimizations
input int MinDataPoints = 1000;    // Minimum number of data points for optimization

// Global variables
CTrade trade;
int fastMA_Handle, slowMA_Handle;
double fastMA[], slowMA[];
int tickCount = 0;
CArrayObj optimizationResults;
int MA_Fast_Period, MA_Slow_Period;

// Optimization ranges
const int MA_Fast_Min = 5, MA_Fast_Max = 50, MA_Fast_Step = 1;
const int MA_Slow_Min = 10, MA_Slow_Max = 100, MA_Slow_Step = 1;

// Class to hold optimization results
class OptimizationResult : public CObject
{
public:
    int fastPeriod;
    int slowPeriod;
    double profit;
};

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    MA_Fast_Period = Initial_MA_Fast_Period;
    MA_Slow_Period = Initial_MA_Slow_Period;

    // Initialize MA handles
    fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
    slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);
    
    if(fastMA_Handle == INVALID_HANDLE || slowMA_Handle == INVALID_HANDLE)
    {
        Print("Failed to create MA indicators");
        return INIT_FAILED;
    }
    
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    // Release MA handles
    IndicatorRelease(fastMA_Handle);
    IndicatorRelease(slowMA_Handle);
    
    // Clean up optimization results
    optimizationResults.Clear();
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    // Check if we have enough bars to calculate MAs
    if(Bars(_Symbol, PERIOD_CURRENT) < MA_Slow_Period) return;
    
    // Copy MA values
    if(CopyBuffer(fastMA_Handle, 0, 0, 2, fastMA) != 2) return;
    if(CopyBuffer(slowMA_Handle, 0, 0, 2, slowMA) != 2) return;
    
    // Auto Optimization
    if(AutoOptimize && ++tickCount >= OptimizationPeriod)
    {
        Optimize();
        tickCount = 0;
    }
    
    // Trading logic
    if(fastMA[1] <= slowMA[1] && fastMA[0] > slowMA[0])
    {
        // Open buy position
        double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        trade.Buy(LotSize, _Symbol, ask, ask - StopLoss * _Point, ask + TakeProfit * _Point);
    }
    else if(fastMA[1] >= slowMA[1] && fastMA[0] < slowMA[0])
    {
        // Open sell position
        double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        trade.Sell(LotSize, _Symbol, bid, bid + StopLoss * _Point, bid - TakeProfit * _Point);
    }
}

//+------------------------------------------------------------------+
//| Optimization function                                            |
//+------------------------------------------------------------------+
void Optimize()
{
    Print("Starting optimization...");
    
    optimizationResults.Clear();
    
    // Loop through all combinations of MA periods
    for(int fastPeriod = MA_Fast_Min; fastPeriod <= MA_Fast_Max; fastPeriod += MA_Fast_Step)
    {
        for(int slowPeriod = MA_Slow_Min; slowPeriod <= MA_Slow_Max; slowPeriod += MA_Slow_Step)
        {
            if(slowPeriod <= fastPeriod) continue; // Slow period should be greater than fast period
            
            double profit = TestParameters(fastPeriod, slowPeriod);
            
            OptimizationResult* result = new OptimizationResult();
            result.fastPeriod = fastPeriod;
            result.slowPeriod = slowPeriod;
            result.profit = profit;
            
            optimizationResults.Add(result);
        }
    }
    
    // Find the best result
    OptimizationResult* bestResult = NULL;
    for(int i = 0; i < optimizationResults.Total(); i++)
    {
        OptimizationResult* currentResult = optimizationResults.At(i);
        if(bestResult == NULL || currentResult.profit > bestResult.profit)
        {
            bestResult = currentResult;
        }
    }
    
    if(bestResult != NULL)
    {
        // Update the EA parameters
        MA_Fast_Period = bestResult.fastPeriod;
        MA_Slow_Period = bestResult.slowPeriod;
        
        // Update indicator handles
        IndicatorRelease(fastMA_Handle);
        IndicatorRelease(slowMA_Handle);
        fastMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Fast_Period, 0, MA_Method, Applied_Price);
        slowMA_Handle = iMA(_Symbol, PERIOD_CURRENT, MA_Slow_Period, 0, MA_Method, Applied_Price);
        
        Print("Optimization complete. New parameters: Fast MA = ", MA_Fast_Period, ", Slow MA = ", MA_Slow_Period);
    }
    else
    {
        Print("Optimization failed to find better parameters.");
    }
}

//+------------------------------------------------------------------+
//| Test a set of parameters                                         |
//+------------------------------------------------------------------+
double TestParameters(int fastPeriod, int slowPeriod)
{
    int maFast = iMA(_Symbol, PERIOD_CURRENT, fastPeriod, 0, MA_Method, Applied_Price);
    int maSlow = iMA(_Symbol, PERIOD_CURRENT, slowPeriod, 0, MA_Method, Applied_Price);
    
    if(maFast == INVALID_HANDLE || maSlow == INVALID_HANDLE)
    {
        Print("Failed to create MA indicators for testing");
        return -DBL_MAX;
    }
    
    double fastBuffer[], slowBuffer[];
    ArraySetAsSeries(fastBuffer, true);
    ArraySetAsSeries(slowBuffer, true);
    
    int copied = CopyBuffer(maFast, 0, 0, MinDataPoints, fastBuffer);
    copied = MathMin(copied, CopyBuffer(maSlow, 0, 0, MinDataPoints, slowBuffer));
    
    if(copied < MinDataPoints)
    {
        Print("Not enough data for testing");
        return -DBL_MAX;
    }
    
    double Close[], Open[];
    ArraySetAsSeries(Close, true);
    ArraySetAsSeries(Open, true);
    copied = CopyClose(_Symbol, PERIOD_CURRENT, 0, copied, Close);
    copied = MathMin(copied, CopyOpen(_Symbol, PERIOD_CURRENT, 0, copied, Open));
    
    double profit = 0;
    for(int i = 1; i < copied; i++)
    {
        if(fastBuffer[i] > slowBuffer[i] && fastBuffer[i-1] <= slowBuffer[i-1])
        {
            // Buy signal
            profit += Close[i-1] - Open[i];
        }
        else if(fastBuffer[i] < slowBuffer[i] && fastBuffer[i-1] >= slowBuffer[i-1])
        {
            // Sell signal
            profit += Open[i] - Close[i-1];
        }
    }
    
    IndicatorRelease(maFast);
    IndicatorRelease(maSlow);
    
    return profit;
}

//+------------------------------------------------------------------+
//| Custom function to log important events                          |
//+------------------------------------------------------------------+
void Log(string message)
{
    Print(TimeToString(TimeCurrent()) + ": " + message);
    
    int handle = FileOpen("EA_Log.txt", FILE_WRITE|FILE_READ|FILE_TXT);
    if(handle != INVALID_HANDLE)
    {
        FileSeek(handle, 0, SEEK_END);
        FileWriteString(handle, TimeToString(TimeCurrent()) + ": " + message + "\n");
        FileClose(handle);
    }
}

Wie Sie diesen Expert Advisor verwenden

  1. Kopieren Sie den gesamten Code in eine neue Datei in MetaEditor.
  2. Speichern Sie die Datei mit der Erweiterung .mq5 (z. B. „AutoOptimizingMA.mq5“).
  3. Kompilieren Sie den EA, indem Sie auf die Schaltfläche „Kompilieren“ klicken oder F7 drücken.
  4. Ziehen Sie in MetaTrader 5 den kompilierten EA auf einen Chart.
  5. Passen Sie die Eingabeparameter nach Bedarf im Einstellungsfenster des EA an.
  6. Aktivieren Sie AutoTrading und lassen Sie den EA laufen.

Hauptmerkmale dieses EA

  1. Crossover-Strategie mit gleitendem Durchschnitt: Der EA verwendet eine grundlegende gleitende Durchschnitts-Crossover-Strategie für Handelsentscheidungen.
  2. Auto-Optimierung: Der EA kann seine Parameter (schnelle und langsame MA-Periodenlängen) auf der Grundlage der jüngsten Marktdaten automatisch optimieren.
  3. Anpassbare Eingaben: Die Nutzer können verschiedene Parameter wie Losgröße, Stop Loss, Take Profit und Optimierungseinstellungen anpassen.
  4. Leistungsprotokollierung: Der EA enthält eine Protokollierungsfunktion, um wichtige Ereignisse und Parameteränderungen zu verfolgen.

Wichtige Hinweise

  • Dieser EA wird als Lehrbeispiel zur Verfügung gestellt und sollte nicht ohne gründliche Tests und Anpassungen für den Live-Handel verwendet werden. Achten Sie auf die Systemressourcen, insbesondere wenn Sie auf einem VPS oder einem lokalen Rechner arbeiten. 
  • Testen Sie den EA immer ausgiebig in einer Demo-Umgebung, bevor Sie den Live-Handel in Betracht ziehen.
  • Die in der Vergangenheit erzielten Ergebnisse sind keine Garantie für zukünftige Ergebnisse. Die Marktbedingungen können sich ändern, was die Leistung des EA beeinträchtigen kann.

Mit diesem EA können Sie erforschen, wie die Auto-Optimierung in der Praxis funktioniert und die Anpassungsfähigkeit Ihrer Handelsstrategie an sich ändernde Marktbedingungen verbessern. Denken Sie daran, die Leistung des Systems ständig zu überwachen und gegebenenfalls Anpassungen vorzunehmen.

Einstellungen


Eingaben

Grafik


Backtesting

Sie können wahrscheinlich bessere Ergebnisse erzielen, indem Sie weitere Bedingungen zu den Aufträgen hinzufügen, wie z. B. Deep Learning oder einen RSI oder was auch immer Sie sich vorstellen können.

Denken Sie daran, dass die Welt des algorithmischen Handels riesig ist und sich ständig weiterentwickelt. Dieser Leitfaden dient als Ausgangspunkt für Ihre Reise zur automatischen Optimierung von Expert Advisors. Wenn Sie Erfahrung sammeln und Ihr Wissen vertiefen, werden Sie zweifellos neue Techniken und Ansätze entdecken, um Ihre Handelssysteme zu verfeinern und zu verbessern.

Viel Glück, und möge Ihr Handel gewinnbringend sein!

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/15837

Beigefügte Dateien |
PSAR, Heiken Ashi und Deep Learning gemeinsam für den Handel nutzen PSAR, Heiken Ashi und Deep Learning gemeinsam für den Handel nutzen
Dieses Projekt erforscht die Verschmelzung von Deep Learning und technischer Analyse, um Handelsstrategien im Forex-Bereich zu testen. Für schnelle Experimente wird ein Python-Skript verwendet, das ein ONNX-Modell neben traditionellen Indikatoren wie PSAR, SMA und RSI einsetzt, um die Entwicklung des EUR/USD vorherzusagen. Ein MetaTrader 5-Skript bringt diese Strategie dann in eine Live-Umgebung und nutzt historische Daten und technische Analysen, um fundierte Handelsentscheidungen zu treffen. Die Backtesting-Ergebnisse deuten auf einen vorsichtigen, aber konsequenten Ansatz hin, bei dem der Schwerpunkt eher auf Risikomanagement und stetigem Wachstum als auf aggressivem Gewinnstreben liegt.
Beispiel für CNA (Causality Network Analysis), SMOC (Stochastic Model Optimal Control) und Nash Game Theory mit Deep Learning Beispiel für CNA (Causality Network Analysis), SMOC (Stochastic Model Optimal Control) und Nash Game Theory mit Deep Learning
Wir werden Deep Learning zu den drei Beispielen hinzufügen, die in früheren Artikeln veröffentlicht wurden, und die Ergebnisse mit den vorherigen vergleichen. Das Ziel ist es, zu lernen, wie man DL zu anderen EAs hinzufügt.
Neuronale Netze leicht gemacht (Teil 91): Vorhersage durch Frequenzbereiche (Frequency Domain Forecasting, FreDF) Neuronale Netze leicht gemacht (Teil 91): Vorhersage durch Frequenzbereiche (Frequency Domain Forecasting, FreDF)
Wir fahren fort mit der Analyse und Vorhersage von Zeitreihen im Frequenzbereich. In diesem Artikel machen wir uns mit einer neuen Methode zur Vorhersage von Daten im Frequenzbereich vertraut, die zu vielen der bisher untersuchten Algorithmen hinzugefügt werden kann.
MQL5-Assistent-Techniken, die Sie kennen sollten (Teil 38): Bollinger Bands MQL5-Assistent-Techniken, die Sie kennen sollten (Teil 38): Bollinger Bands
Bollinger Bänder sind ein sehr gebräuchlicher Hüllkurven-Indikator, der von vielen Händlern verwendet wird, um Trades manuell zu platzieren und zu schließen. Wir untersuchen diesen Indikator, indem wir möglichst viele der verschiedenen möglichen Signale betrachten, die er erzeugt, und sehen, wie sie in einem von einem Assistenten zusammengestellten Expert Advisor verwendet werden können.