![Eine generische Optimierungsformulierung (GOF) zur Implementierung von Custom Max mit Nebenbedingungen](https://c.mql5.com/2/76/A_Generic_Optimization_Formulation_rGOF2_to_Implement_Custom_Max_with_Constraints_600x314.jpg)
Eine generische Optimierungsformulierung (GOF) zur Implementierung von Custom Max mit Nebenbedingungen
Einführung - Grundlagen der Optimierung
Optimierungsprobleme haben zwei Phasen: 1) Problemformulierung und 2) Problemlösung. In der ersten Phase gibt es drei Hauptkomponenten: die Eingabevariablen, die Zielfunktionen und die Nebenbedingungsfunktionen. In der zweiten Phase wird die Lösung des Problems numerisch mit einem Optimierungsalgorithmus durchgeführt.
Variablen (Notation x_1 , x_2 , x_3 , ..., x_n): Die Variablen sind die „Knöpfe“, die wir verändern können, um die Zielfunktion zu maximieren. Es gibt Variablen verschiedener Typen: Integer, Real und Boolean. In Expert Advisors können wir Variablen wie den gleitenden Durchschnitt, das TP/SL-Verhältnis beim Einstieg, den SL in Pips usw. verwenden.
Zielfunktionen (Notation f_i (x)): Wenn es mehr als eine Zielfunktion gibt, wird das Problem als Mehrzieloptimierungsproblem bezeichnet. Der Optimierungsalgorithmus des MetaTrader 5 erwartet nur eine Zielfunktion, daher müssen wir, um mehr als ein Ziel zu berücksichtigen, diese zu einer Funktion kombinieren. Die einfachste Art, Zielfunktionen zu kombinieren, besteht darin, eine gewichtete Summe aus ihnen zu bilden. Einige Entwickler haben vorgeschlagen, die Multiplikation zu verwenden, um sie zu kombinieren, und das kann in einigen Situationen funktionieren, aber wir bevorzugen die Summation als besseren Ansatz. In diesem Artikel verwenden wir eine „gezielte und gewichtete“ Zusammenfassung von Zielen, wie im Folgenden erläutert wird. Beispiele für Zielfunktionen sind: Bilanz, Gewinnziel, Gewinnrate, Jahresrendite, Nettogewinn usw. Nebenbedingungsfunktionen (Notation g_i (x)): Dies sind Funktionen, die einen Wert erzeugen, den der Nutzer binden möchte. Die Schranke könnte eine obere Schranke sein, wie in g_i(x) ≤ U_i , wobei g_i(x) die i_te Nebenbedingung und U_i die obere Schranke ist. In ähnlicher Weise ist eine untere Schranke wie g_i(x) ≥ L_i , wobei L_i die untere Schranke ist.Der MetaTrader 5-Optimierungsalgorithmus berücksichtigt nur die Nebenbedingungen, die den Eingabevariablen auferlegt werden (auch als Randbedingungen bekannt), z. B. 3 <= x_1 <= 4, aber kein anderer Nebenbedingungstyp kann verwendet werden. Daher müssen wir das Vorhandensein jeder zusätzlichen Nebenbedingung g_i(x) in die endgültige und einzige Zielfunktion F(x) einbeziehen, wie unten gezeigt wird. Beispiele für Nebenbedingungsfunktionen g_i(x) sind: Begrenzung der Anzahl der aufeinanderfolgenden Verluste, der Sharpe-Ratio, der Gewinnquote usw.
Optimierungsalgorithmen
Im Allgemeinen gibt es zwei Haupttypen von Optimierungsalgorithmen. Der erste Typ ist der klassischere, der auf der Berechnung der Gradienten aller am Optimierungsproblem beteiligten Funktionen beruht (dies geht auf die Zeit von Isaac Newton zurück). Der zweite Typ ist neueren Datums (seit den 1970er Jahren) und verwendet überhaupt keine Gradienteninformationen. Dazwischen kann es Algorithmen geben, die die beiden genannten Ansätze kombinieren, aber auf diese brauchen wir hier nicht einzugehen. Der MetaTrader 5 Algorithmus mit der Bezeichnung „Schneller Genetischer Algorithmus“ --- in der Registerkarte MetaTrader 5 Terminal Eingaben --- gehört zum zweiten Typ. Auf diese Weise können wir auf die Berechnung von Gradienten für Ziel- und Nebenbedingungsfunktionen verzichten. Mehr noch, dank der gradientenlosen Natur des MetaTrader 5 Algorithmus waren wir in der Lage, Nebenbedingungen zu berücksichtigen, die bei gradientenbasierten Algorithmen nicht angemessen gewesen wären. Darauf wird weiter unten eingegangen.
Ein wichtiger Punkt ist, dass der MetaTrader 5-Algorithmus „Langsamer Vollständiger Algorithmus“ eigentlich kein Optimierungsalgorithmus ist, sondern ein Brute-Force-Algorithmus, der alle möglichen Kombinationen von Werten für alle Eingabevariablen innerhalb der Nebenbedingungen auswertet.
Die Zielfunktion F(x), gebildet aus mehreren Zielen f_i(x)
In diesem Abschnitt werden wir die Kombination mehrerer Zielfunktionen zu einer einzigen Zielfunktion F behandeln.
Wie in der Einleitung erwähnt, verwenden wir die Summierung, um mehrere Ziele zu kombinieren. Der Leser ist eingeladen, den Quellcode zu ändern, um eine multiplikative Kombination zu verwenden, wenn er dies wünscht. Die Formel für die Summenkombination lautet:
wobei
- x ist der Vektor der n Eingangsvariablen
- f_i(x) ist die i-te Zielfunktion
- W_i ist das i_te Gewicht
- T_i ist das i_te Ziel, das für die i_te Zielfunktion angestrebt wird
Die Zielvorgabe dient als Normalisierungswert (Teilungswert), damit ein bestimmtes Ziel mit anderen Zielen in der Summierung vergleichbar ist. Mit anderen Worten: Die gewichtete Summe gilt für „normalisierte“ Zielfunktionen. Das Gewicht dient als solches, als ein Gewicht, das das normierte Ziel ( f_i(x)/T_i ) multipliziert, sodass der Nutzer in der Lage ist, hervorzuheben, welches normierte Ziel wichtiger ist als das andere. Die Gewichte W_i werden in eine konvexe Form gebracht, indem die Summe von W_i*f_i(x)/T_i durch die Summe der W_i geteilt wird.
Auswahl von W_i: Die Konvexität der Gewichte ermöglicht es dem Nutzer, sich auf ihren relativen Wert und nicht auf ihren absoluten Wert zu konzentrieren. Nehmen wir zum Beispiel an, es gibt drei Ziele: jährliche Rendite, Erholungsfaktor und Gewinnfaktor. Diese Gewichte: W_1=10, W_2=5, W_3=1, haben die gleiche Wirkung wie W_1=100, W_2=50, W_3=10, d.h. das normalisierte Ziel 1 (Jahresrendite/T_1) wird als doppelt so wichtig wie das normalisierte Ziel 2 (Erholungsfaktor/T_2) und zehnmal wichtiger als das normalisierte Ziel 3 (Gewinnfaktor/T_3) angesehen. Es gibt keine richtigen oder falschen Werte, solange sie positiv sind und die relative Bedeutung auf der Grundlage der Kriterien des Nutzers ausdrücken.
Auswahl von T_i: Die Auswahl der Ziele T_i muss nach einigen Simulationen sorgfältig erfolgen, bevor die vollständige Optimierung durchgeführt wird. Der Grund dafür ist, die Bereiche jeder Zielfunktion abzuschätzen und T_i-Werte festzulegen, die normalisierte Funktionen (f_i(x)/T_i) von vergleichbarer Größe ergeben. Nehmen wir zum Beispiel an, Ihr Konto hat einen Anfangssaldo von 10.000; Ihr EA-Algorithmus stellt den Endsaldo auf 20.000, bevor er optimiert wird; der Gewinnfaktor beträgt 0,9. Wenn Sie Ihr Optimierungsproblem mit zwei Zielen aufstellen: f_1 = Gleichgewicht, f_2 = Gewinnfaktor, dann sind gute Werte für Ziele: T_1 = 30k, T_2 = 2, sodass beide normierten Funktionen die gleiche Größenordnung haben (vergleichbare Werte). Wenn Sie eine vollständige Optimierung durchführen, werden Sie feststellen, dass der EA einen sehr hohen Endsaldo und einen ähnlichen Gewinnfaktor aufweist. An dieser Stelle könnten Sie die T_i-Werte anpassen, wenn die normalisierten Funktionen unterschiedliche Größenordnungen haben. Die Zielwerte müssen ebenfalls positive reelle Zahlen sein. Nutzen Sie Ihr Urteilsvermögen. Mehr zu diesem Thema erfahren Sie, wenn wir uns mit der Ausgabe des GOF-Codes befassen.
Hinzufügen von Nebenbedingungen
Die Formulierung der Zielfunktion als gewichtete Summe der Einzelziele ist so gut wie Standard. Wir stellen nun einen einfachen Weg vor, wie man Nebenbedingungsfunktionen so in das Optimierungsproblem einbeziehen kann, dass der Schnelle Genetische Algorithmus des MetaTrader 5 trotzdem funktioniert. Wir werden eine modifizierte Version der Lagrange-Methode verwenden, bei der eine Strafe von dem einzelnen Optimierungsziel abgezogen wird. Zu diesem Zweck werden wir neue Straffunktionen definieren, die das Ausmaß der Verletzung jeder einzelnen Nebenbedingung messen:
Wie Sie überprüfen können, ist P_i(x) positiv, wenn g_i(x) verletzt wird, und ansonsten Null. Wenn U_i oder L_i Null sind, ersetzen wir sie durch 1, um eine Division durch Null zu vermeiden.
Bei der Lagrange-Multiplikator-Methode gibt es einen Multiplikator pro Nebenbedingung, und ihre Werte sind die Lösung eines Gleichungssystems. In unserer modifizierten Version gehen wir davon aus, dass alle Multiplikatoren gleich sind und einen konstanten Wert k_p haben (siehe Formel unten). Diese Vereinfachung funktioniert hier, weil der Optimierungsalgorithmus keine Gradienten irgendeiner Funktion im Optimierungsproblem zu berechnen braucht.Die endgültige einzelne Zielfunktion F(x) lautet:
Hinweis: Das Symbol „:=“ bedeutet Zuweisung, nicht mathematische Definition.
Der Multiplikator k_p spielt die Rolle der Lagrange-Multiplikatoren und wird verwendet, um nicht durchführbare Entwürfe (die mindestens eine Nebenbedingung verletzen) zu einem sehr niedrigen Ziel zu zwingen. Diese Herabsetzung des Zielwertes führt dazu, dass der genetische Algorithmus in MetaTrader 5 dieses Design sehr niedrig einstuft und es unwahrscheinlich ist, dass es für die Reproduktion in der nächsten Generation von Designs verwendet wird.
Der Multiplikator k_o ist nicht Teil der Lagrange-Multiplikator-Methode. Er wird verwendet, um die Zielfunktion für machbare Designs zu erhöhen, um die positive Seite der Y-Achse in den Optimierungsplots im MetaTrader 5 Terminal zu erweitern.
Der Nutzer kann die Werte für K_o und K_p in der Eingabe-Sektion „Miscellaneous“ ändern. Wir empfehlen, dass die Werte von k_o und K_p Potenzen von 10 sind (z.B. 10, 100, 1000, etc.).
Screenshot der Registerkarte Eingaben des MetaTrader 5-Terminals
Es gibt drei Eingabebereiche in GOF: Zielfunktionen, Nebenbedingungsfunktionen und Sonstiges.
Nachfolgend finden Sie den Abschnitt Objective Functions (Zielfunktionen). Es gibt bis zu 5 Ziele, die in die Problemformulierung aufgenommen werden können, von 18 möglichen Funktionen, die später gezeigt werden. Das Hinzufügen von mehr als 5 Zielen kann gemäß dem Code erfolgen, aber alles, was über 3 Ziele hinausgeht, erschwert die Auswahl von Gewichten und Zielen. Die Hinzufügung von mehr als 18 möglichen Funktionen kann anhand des Codes erfolgen.
Nachfolgend finden Sie den Abschnitt über die Hard-Constraint (harte Nebenbedingungen). Es gibt bis zu 10 Nebenbedingungen, die der Optimierungsformulierung hinzugefügt werden können, von den 14 in GOF implementierten Nebenbedingungen. Die Hinzufügung von mehr als 10 Nebenbedingungen kann gemäß dem Code erfolgen. Das Hinzufügen neuer Nebenbedingungen zur Liste der 14 kann auch mit Hilfe des Codes erfolgen.
Nachfolgend finden Sie den Abschnitt Miscellaneous Optimization Parameter (verschiedene Optimierungsparameter). Auf diesen Abschnitt wird in den nächsten Abschnitten näher eingegangen.
Verwendung von GenericOptimizationFormulation.mqh in Ihrem EA
Wenn Sie zu den Menschen gehören, die wenig Geduld haben, um den ganzen Artikel zu lesen, stellen wir Ihnen zunächst die Schritte zur Verwendung dieses Codes in Ihrem EA zur Verfügung, damit Sie zunächst damit spielen können, und lesen Sie dann den Rest des Artikels mit mehr Einblick. Nachfolgend finden Sie die ersten Kommentare in der Datei GenericOptimizationFormulation.mqh:
/*
In order to use this file in your EA you must do the following:
- Edit the EA .mq5 file
- Insert the following two lines after the input variables and before OnInit()
ulong gof_magic= an_ulong_value;
#include <YOUR_INCLUDE_LOCATION\GenericOptimizationFormulation.mqh>
- Save and compile your EA file
If you get compiler's errors, make sure you did:
- replace an_ulong_value by the variable containing the magic number, or by the magic numeric value
- replace YOUR_INCLUDE_LOCATION by the folder name where your include files are
*/
Ich hoffe, der obige Einschub ist selbsterklärend. Wir werden später ein Beispiel mit dem MetaTrader 5 Moving Average EA zeigen. Lassen Sie uns nun mit der Erläuterung des Quellcodes fortfahren.
Der Quellcode: GenericOptimizationFormulierung.mqh
Nachdem wir nun die Formeln für die Zielfunktion und die Nebenbedingungen vorgestellt haben, besprechen wir die mql5-Implementierung mit Ausschnitten aus dem Code.
Bitten an die Leser:
- Man sagt, dass eine Software mit mehr als sieben Codezeilen eine Wahrscheinlichkeit ungleich Null hat, einen Fehler zu haben. GOF hat mehr als tausend Zeilen, die Wahrscheinlichkeit ist also definitiv nicht gleich Null. Wir ermutigen die Leser, in den Kommentaren Feedback zu geben, um den Code zu verbessern.
- Wenn Sie einen besseren Weg finden, den Code zu schreiben, freuen wir uns darauf, Ihre besseren Codezeilen zu sehen, um GOF zu verbessern.
- Ergänzen und erweitern Sie den Code um Ihre eigenen Zielfunktionen und Nebenbedingungen. Bitte teilen Sie sie auch in den Kommentaren mit.
Zunächst müssen wir einige Bibliotheken für Statistik und Algebra einbinden:
#include <Math\Stat\Lognormal.mqh> #include <Math\Stat\Uniform.mqh> #include <Math\Alglib\alglib.mqh>
Objektive Funktionen zur Auswahl:
enum gof_FunctionDefs { // functions to build objective MAX_NONE=0, // 0] None MAX_AnnRetPct, // 1] Annual Return % MAX_Balance, // 2] Balance MAX_NetProfit, // 3] Net Profit MAX_SharpeRatio, // 4] Sharpe Ratio MAX_ExpPayOff, // 5] Expected Payoff MAX_RecovFact, // 6] Recovery Factor MAX_ProfFact, // 7] Profit Factor MAX_LRcrr3, // 8] LRcrr^3 MAX_NbrTradesPerWeek, // 9] #Trades/week MAX_WinRatePct, // 10] Win Rate % MAX_Rew2RiskRatio, // 11] Reward/Risk(RRR=AvgWin/AvgLoss) MAX_OneOverLRstd, // 12] 1/(LR std%) MAX_OneHoverWorstTradePct, // 13] 100/(1+|WorstLoss/Init.Dep*100|) MAX_LR, // 14] LRslope*LRcorr/LRstd MAX_OneHoverEqtyMaxDDpct, // 15] 100/(1+EqtyMaxDD%)) MAX_StratEfficiency, // 16] Seff=Profit/(TotalTrades*AvgLot) MAX_KellyCrit, // 17] Kelly Criterion MAX_OneOverRoRApct // 18] 1/Max(0.01,RoRA %) };
Es gibt 18 mögliche Ziele, von denen maximal 5 zu wählen sind, um das Optimierungsproblem zu formulieren. Der Nutzer kann dem Quellcode weitere Funktionen hinzufügen, die dem gleichen Implementierungsmuster folgen. Die Namen der Zielfunktionen sollen selbsterklärend sein, mit Ausnahme einiger weniger, die im Folgenden genannt werden:
- LRcrr^3: der lineare Regressionskorrelationskoeffizient hoch 3.
- 1/(LR std%): LR std ist die Standardabweichung der linearen Regression. Der Kehrwert misst, wie eng die Equity-Linie an einer geraden Linie liegt.
- 100/(1+|WorstLoss/Init.Dep*100|): Der schlimmste Verlust geteilt durch die anfängliche Einlage ist ein Maß für schlechte Performance. Der umgekehrte Fall ist ein Maß für gute Leistung.
- LRslope*LRcorr/LRstd: Dies ist ein multiplikatives Ziel aus drei Funktionen der linearen Regression: die Steigung, der Korrelationskoeffizient und die Standardabweichung.
- Seff=Gewinn/(TotalTrades*AvgLot): ist ein Maß für die Strategieeffizienz. Wir bevorzugen Strategien mit hohem Gewinn, einer geringen Anzahl von Positionen und einer kleinen Losgröße.
- 1/Max(0,01,RoRA %): RoRA ist das Risiko des Ruins Ihres Kontos. Dies wird mit Hilfe einer Monte-Carlo-Simulation berechnet, auf die wir später noch eingehen werden.
Harte Nebenbedingungen (Hard Constraints) zur Auswahl:
enum gof_HardConstrains { hc_NONE=0, // 0] None hc_MaxAccountLoss_pct, // 1] Account Loss % InitDep hc_maxAllowed_DDpct, // 2] Equity DrawDown % hc_maxAllowednbrConLossTrades, // 3] Consecutive losing trades hc_minAllowedWin_pct, // 4] Win Rate % hc_minAllowedNbrTradesPerWeek, // 5] # trades/week hc_minAllowedRecovFactor, // 6] Recov Factor hc_minAllowedRRRFactor, // 7] Reward/Risk ratio hc_minAllowedAnnualReturn_pct, // 8] Annual Return in % hc_minAllowedProfFactor, // 9] Profit Factor hc_minAllowedSharpeFactor, // 10] Sharpe Factor hc_minAllowedExpPayOff, // 11] Expected PayOff hc_minAllowedMarginLevel, // 12] Smallest Margin Level hc_maxAllowedTradeLoss, // 13] Max Loss trade hc_maxAllowedRoRApct // 14] Risk of Ruin(%) }; enum gof_HardConstType { hc_GT=0, // >= Greater or equal to hc_LT // <= Less or equal to };Es gibt 14 mögliche Nebenbedingungen, von denen maximal 10 zu wählen sind, um das Optimierungsproblem zu formulieren. Der Nutzer kann dem Quellcode weitere Nebenbedingungen hinzufügen, die dem gleichen Implementierungsmuster folgen. Die Bezeichnungen der Nebenbedingungsfunktionen sollen selbsterklärend sein. Es gibt zwei Arten von Nebenbedingungen, hc_GT für Nebenbedingungen mit einer unteren Schranke und ht_LT für Nebenbedingungen mit einer oberen Schranke. Mehr dazu, wenn wir zeigen, wie man sie nutzt.
Risiko des Ruins Optionen
enum gof_RoRaCapital { roraCustomPct=0, // Custom % of Ini.Dep. roraCustomAmount, // Custom Capital amount roraIniDep // Initial deposit };
Bei der Berechnung des Risikos, dass Ihr Konto ruiniert wird, gibt es drei Möglichkeiten, das „Konto“ Geld zu definieren. Die erste Option ist ein Prozentsatz der Ersteinlage. Die zweite Option ist ein fester Kapitalbetrag, der in Ihrer Kontowährung angegeben wird. Die dritte Option ist der Sonderfall der ersten Option, wenn der Prozentsatz 100 % beträgt. Das Risiko des Ruins wird später mit dem Code erklärt.
Zielfunktion Dezimalstellen
Wie bereits erwähnt, können wir mit Hilfe der beiden Dezimalstellen in der Ergebnisspalte zusätzliche Informationen aus der Simulation anzeigen. Hier sind die Optionen:
enum gof_objFuncDecimals { fr_winRate=0, // WinRate % fr_MCRoRA, // MonteCarlo Sim Risk of Ruin Account % fr_LRcorr, // LR correlation fr_ConLoss, // Max # Consecutive losing Trades fr_NONE // None };
fr_winRate ist die prozentuale Gewinnrate der Simulation in Prozent. Wenn die Gewinnquote beispielsweise 34 % beträgt, ist das Ergebnisziel 0,39. Wenn die Gewinnrate 100% beträgt, wird 0,99 angezeigt.
fr_MCRoRA ist das Risiko des Ruins des Kontos in Prozent. Beträgt das Risiko, das Konto zu ruinieren, zum Beispiel 11 %, so ist das Ergebnisziel 0,11.
fr_LRcorr ist der lineare Regressionskorrelationskoeffizient. Wenn die Gewinnquote beispielsweise 0.88 % beträgt, ist das Ergebnisziel 0,88.
fr_ConLoss ist die größte Anzahl von kontinuierlichen Verlustgeschäften. Wenn die Gewinnquote beispielsweise 7 % beträgt, ist das Ergebnisziel 0,07. Wenn die Zahl größer als 99 ist, wird 0,99 angezeigt.
fr_NONE wird verwendet, wenn Sie keine Informationen in den Dezimalzahlen sehen wollen.
Zielfunktionen
Der nächste Abschnitt des Codes ist die Auswahl der einzelnen Zielfunktionen (maximal 5). Nachfolgend finden Sie einen Ausschnitt nur der ersten Funktion, zusammen mit ihrem Ziel und ihrem Gewicht.
input group "- Build Custom Objective to Maximize:" sinput gof_FunctionDefs gof_Func1 = MAX_AnnRetPct; // Select Objective Function to Maximize 1: sinput double gof_Target1 = 200; // Target 1 sinput double gof_Weight1 = 1; // Weight 1
Nebenbedingungsfunktionen
input group "- Hard Constraints:" sinput bool gof_IncludeHardConstraints = true;//if false, all constraints are ignored sinput gof_HardConstrains gof_HC_1=hc_minAllowedAnnualReturn_pct; // Select Constraint Function 1: sinput gof_HardConstType gof_HCType_1=hc_GT; // Type 1 sinput double gof_HCBound_1=50; // Bound Value 1
Es gibt eine Option, um alle harten Nebenbedingungen auszuschalten, indem man gof_IncludeHardConstraints=false setzt. Als Nächstes werden die erste Nebenbedingung, ihr Typ und ihr gebundener Wert ausgewählt. Alle zehn Nebenbedingungen haben das gleiche Format.
Verschiedene Optimierungsparameter
input group "------ Misc Optimization Params -----" sinput gof_objFuncDecimals gof_fr = fr_winRate; // Choose Result-column's decimals sinput gof_RoRaCapital gof_roraMaxCap = roraCustomPct; // Choose capital method for Risk of Ruin sinput double gof_RoraCustomValue = 10; // Custom Value for Risk of Ruin (if needed) sinput bool gof_drawSummary = false; // Draw summary on chart sinput bool gof_printSummary = true; // Print summary on journal sinput bool gof_discardLargestProfit = false; // Subtract Largest Profit from Netprofit sinput bool gof_discardLargestLoss = false; // Add Largest Loss to Net profit sinput double gof_PenaltyMultiplier = 100; // Multiplier for Penalties (k_p) sinput double gof_ObjMultiplier = 100; // Multiplier for Objectives (k_o)
Im obigen Abschnitt wählt der Nutzer:
- gof_fr: Die Menge, die als Dezimalzahlen in der Spalte Ergebnis angezeigt werden soll.
- gof_roraMaxCap: Die Methode zur Berechnung des RoRA-Kapitals.
- gof_RoraCustomValue: Der Wert des Kapitals oder der Prozentsatz der Ersteinlage für RoRA. Dies hängt von Ihrer Auswahl in der vorherigen Zeile ab.
- gof_drawSummary: Sie können die Zusammenfassung des GOF-Berichts in das Chart einzeichnen.
- gof_printSummary: Sie können die Zusammenfassung des GOF-Berichts auf der Registerkarte Journal ausdrucken.
- gof_discardLargestProfit: Sie können den größten Gewinn vom Nettogewinn abziehen, um Strategien zu verhindern, die einen einzigen großen Gewinn begünstigen.
- gof_discardLargestLoss: Sie können den größten Verlust zum Nettogewinn addieren, um Strategien mit großen Verlusten zu verhindern.
- gof_PenaltyMultiplier: der Multiplikator „K_p“, der oben bei der Definition der Zielfunktion angegeben wurde.
- gof_ObjMultiplier: der Multiplikator „K_o“, der oben bei der Definition der Zielfunktion angegeben wurde.
Die Standardwerte im Abschnitt „Verschiedenes“ sollten gut funktionieren.
Die nächsten Zeilen des Codes dienen der Definition von Variablen und dem Abrufen von Werten aus der Funktion MetaTrader 5 TesterStatistics(). Danach kommt der GOF-Hauptteil:
//------------ GOF ---------------------- // Printing and displaying results from the simulation GOFsummaryReport(); // calculate the single objective function double SingleObjective = calcObjFunc(); // calculate the total penalty from constraint violations if(gof_IncludeHardConstraints) gof_constraintTotalPenalty=calcContraintTotalPenalty(gof_displayContraintFlag); // Compute customMaxCriterion // gof_PenaltyMultiplier pushes infeasible designs to have low objective values // gof_PenaltyMultiplier expand the positive side of the Y axis double customMaxCriterion=gof_constraintTotalPenalty>0? SingleObjective-gof_PenaltyMultiplier*gof_constraintTotalPenalty: gof_ObjMultiplier*SingleObjective; // add additional simulation result as two decimal digits in the result column customMaxCriterion=AddDecimalsToCustomMax(customMaxCriterion); // Printing and displaying more results from GOF FinishGOFsummaryReport(customMaxCriterion); return (NormalizeDouble(customMaxCriterion,2));
Der obige Code zeigt:
- GOFsummaryReport(), um den GOF-Zusammenfassungsbericht zu erstellen, der auf der Registerkarte Journal und im Chart erscheint.
- calcObjFunc(), um die kombinierte einzelne Zielfunktion zu berechnen.
- calcContraintTotalPenalty(), um die Gesamtstrafe aufgrund von Verletzungen der Nebenbedingungen zu berechnen.
- customMaxCriterion wird dann, wie in der Einleitung dargestellt, als Summe der Einzelziele abzüglich der Gesamtstrafe für die Verletzung von Nebenbedingungen berechnet.
- AddDecimalsToCustomMax() wird verwendet, um die Informationen in den Dezimalstellen von customMaxCriterion hinzuzufügen.
- FinishGOFsummaryReport() beendet und druckt den GOF-Zusammenfassungsbericht.
Der Rest des Codes ist eine direkte Umsetzung der in der Einleitung genannten Formeln. Der einzige Teil, der diskussionswürdig ist, ist die Berechnung des Risikos eines Ruins.
Risiko eines Ruins durch Monte-Carlo-Simulationen
Das Ruinrisiko könnte mit einer Formeleinfachen Formel berechnet werden, aber wir haben uns stattdessen für eine Monte-Carlo-Simulation entschieden, da die einfache Formel keine vernünftigen Ergebnisse lieferte. Für den Monte-Carlo-Ansatz benötigen wir den durchschnittlichen Gewinn und Verlust, die Standardabweichungen dieser Gewinne und Verluste, die Gewinnquote und die Anzahl der Geschäfte in der Simulation. Außerdem müssen wir das Kapital bereitstellen, das den Ruin des Kontos bestimmt.
double MonteCarlo_RiskOfRuinAccount(double WinRatePct, double AvgWin, double AvgLoss, double limitLoss_money, int nTrades) { // 10000 Montecarlo simulations, each with at least 100 trades. // Ideally, if we had lots of trades in the history, we could use a Markov Chain transfer probability matrix // we are limiting the statistics to mean & stdev, without knowledge of a transfer probability information double posDealsMean,posDealsStd,negDealsMean,negDealsStd; CalcDealStatistics(gof_dealsEquity, posDealsMean,posDealsStd,negDealsMean,negDealsStd); // seeding the random number generator MathSrand((int)TimeLocal()+1); // ignore posDealsMean and negDealsMean. Use AvgWin and AvgLoss instead AvgLoss=MathAbs(AvgLoss); WinRatePct=MathMin(100,MathMax(0,WinRatePct)); // case when win rate is 100%: if((int)(WinRatePct*nTrades/100)>=nTrades) { WinRatePct=99; // just to be a bit conservative if winrate=100% AvgLoss=AvgWin/2; // a guessengineering value negDealsStd=posDealsStd;// a guessengineering value } // Use log-normal distribution function. Mean and Std are estimated as: double win_lnMean =log(AvgWin*AvgWin/sqrt(AvgWin*AvgWin+posDealsStd*posDealsStd)); double loss_lnMean=log(AvgLoss*AvgLoss/sqrt(AvgLoss*AvgLoss+negDealsStd*negDealsStd)); double win_lnstd =sqrt(log(1+(posDealsStd*posDealsStd)/(AvgWin*AvgWin))); double loss_lnstd =sqrt(log(1+(negDealsStd*negDealsStd)/(AvgLoss*AvgLoss))); double rand_Win[],rand_Loss[]; double r[]; // limit amount of money that defines Ruin limitLoss_money=MathAbs(limitLoss_money); bool success; int ruinCount=0; // counter of ruins int successfulMCcounter=0; int nTradesPerSim=MathMax(100,nTrades);// at least 100 trades per sim int nMCsims=10000; // MC sims, each one with nTradesPerSim for(int iMC=0; iMC<nMCsims; iMC++) { success=MathRandomUniform(0,1,nTradesPerSim,r); // generate nTradesPerSim wins and losses for each simulation // use LogNormal distribution success&=MathQuantileLognormal(r,win_lnMean,win_lnstd,true,false,rand_Win); success&=MathQuantileLognormal(r,loss_lnMean,loss_lnstd,true,false,rand_Loss); if(!success)continue; successfulMCcounter++; //simulate nTradesPerSim double eqty=0; // start each simulation with zero equity for(int i=0; i<nTradesPerSim; i++) { // draw a random number in [0,1] double randNumber=(double)MathRand()/32767.; // select a win or a loss depending on the win rate and the random number // and add to the equity eqty+=randNumber*100 < WinRatePct? rand_Win[i]: -rand_Loss[i]; // check if equity is below the limit (ruin) // count the number of times there is a ruin if(eqty<= -limitLoss_money) { ruinCount++; break; } } } // compute risk of ruin as percentage double RiskOfRuinPct=(double)(ruinCount)/successfulMCcounter*100.; return(RiskOfRuinPct); }
Zum besseren Verständnis haben wir viele Kommentarzeilen in den obigen Code eingefügt. Mit der Funktion CalcDealStatistics(), die ebenfalls im Quellcode enthalten ist, werden die Standardabweichungen der Gewinne und Verluste berechnet. Die Hauptannahme bei der Berechnung des Ruinrisikos ist, dass die Historie der Handelsgeschäfte einer Log-Normal-Verteilung folgt, um sicherzustellen, dass die Stichproben der Verteilung positive und negative Werte für Gewinne bzw. Verluste aufweisen.
Im Idealfall könnten wir, wenn wir viele Handelsgeschäfte in der Historie hätten, eine Markov-Ketten-Übertragungswahrscheinlichkeitsmatrix verwenden, anstatt die „Log-Normalität“ der Handelsgeschäftshistorie anzunehmen. Da die Geschäftshistorie höchstens ein paar hundert Handelsgeschäfte umfasst, gibt es nicht genügend Informationen, um die Wahrscheinlichkeitsmatrix für den Übergang in die Markov-Kette mit guter Genauigkeit zu erstellen.
Interpretation von MC Risk of Ruin
Das Ergebnis einer Monte-Carlo-Simulation muss als Wahrscheinlichkeit für das Risiko eines Kapitalverlusts interpretiert werden, nicht als Vorhersagewert. Wenn der Rückgabewert der Monte-Carlo-Simulation beispielsweise 1 % beträgt, bedeutet dies, dass die Wahrscheinlichkeit 1 % beträgt, dass Ihre Handelsstrategie Ihr gesamtes Risikokapital vernichten wird. Das bedeutet nicht , dass Sie 1 % des Risikokapitals verlieren.
Hinzufügen von Dezimalstellen zu Custom Max
Das ist eine knifflige Angelegenheit. Wenn der Leser einen besseren Weg findet, teilen Sie ihn bitte mit. Sobald die Dezimalwerte berechnet sind (im Code mit dec bezeichnet), wird das Ziel (obj) wie folgt geändert:
obj=obj>0? MathFloor(obj*gof_ObjPositiveScalefactor)+dec/100.: -(MathFloor(-obj*gof_ObjNegativeScalefactor)+dec/100.);
Wie Sie sehen können, wird obj, wenn es sich um einen positiven Wert handelt, mit einer großen Zahl multipliziert (gof_ObjPositiveScalefactor=1e6), abgeschnitten, und dann wird der Wert „dec“ durch 100 geteilt und als Dezimalzahl addiert. Wenn der Objektwert negativ ist (was bedeutet, dass viele Nebenbedingungen verletzt wurden), wird das Objekt mit einer anderen Zahl multipliziert (gof_ObjPositiveScalefactor =1e3), um die vertikale Achse für negative Werte zu komprimieren.
Implementierung von GOF in den MetaTrader 5 Moving Average EA
Hier ist ein Beispiel, um zu zeigen, wie man GOF in den MetaTrader 5 Moving Averages.mq5 Expert Advisor implementiert:
//+------------------------------------------------------------------+ //| Moving Averages.mq5 | //| Copyright 2000-2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2000-2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include <Trade\Trade.mqh> input double MaximumRisk = 0.02; // Maximum Risk in percentage input double DecreaseFactor = 3; // Descrease factor input int MovingPeriod = 12; // Moving Average period input int MovingShift = 6; // Moving Average shift //--- int ExtHandle=0; bool ExtHedging=false; CTrade ExtTrade; #define MA_MAGIC 1234501 // changes needed to use the GenericOptimizationFormulation (GOF): ulong gof_magic= MA_MAGIC; #include <GenericOptimizationFormulation.mqh> // end of changes
Das war's! In der EA-Datei müssen nur zwei Zeilen hinzugefügt werden.
Optimierungsbeispiele mit GOF
Beispiel 1: Ein Ziel, keine Nebenbedingungen (als ob wir MetaTrader 5 „Balance max“ in der Registerkarte Einstellungen verwenden würden):
Die Registerkarte „Eingabe“ befindet sich unten. Da es nur ein einziges Ziel gibt, sind die Zielvorgabe und die Gewichtung überhaupt nicht relevant. Beachten Sie, dass mit der ersten booleschen Variablen im Abschnitt „Hard Constraints“ alle Nebenbedingungen ausgeschaltet werden:
Der Abschnitt „Miscellaneous“ ist:
Die Ergebnisse sind nachstehend aufgeführt. In der Spalte „Result“ sehen Sie, dass es sich nicht um den Kontostand handelt, sondern um das geänderte Ziel mit der Gewinnrate in Dezimalzahlen.
Wir sehen, dass die beste Kombination von Variablen (9,9,3) einen Gewinn von 939,81 und eine Gewinnrate von 41% ergibt.
Wir haben die optimale Kombination (erste Zeile) in der Tabelle simuliert. Der GOF-Zusammenfassungsbericht wird auf der Registerkarte Journal gedruckt:
Im nächsten Beispiel werden wir den GOF-Zusammenfassungsbericht genauer betrachten.
Beispiel 2: Drei Ziele und fünf Nebenbedingungen
Die Registerkarte „Einstellungen“ ist dieselbe wie zuvor. Die Eingabevariablen und -bereiche sind ebenfalls identisch. Die Eingaberegisterkarte für GOF-Variablen ist unten dargestellt.
Zielsetzungen:
- Jährliche Rendite bei einem Ziel von 50% und einer Gewichtung von 100
- Der Erholungsfaktor mit einem Zielwert von 10 und einem Gewicht von 10
- Die prozentuale Gewinnrate mit einem Ziel von 100% und einer Gewichtung von 10
Nebenbedingungen:
- Kontoverlust in % der ursprünglichen Einlage kleiner oder gleich 10%.
- % Kapital-Drawdown kleiner oder gleich 10%.
- Die Anzahl der aufeinanderfolgenden Verlustgeschäfte muss kleiner oder gleich 5 sein.
- Die Gewinnquote muss mindestens 35 % betragen.
- Das Risiko des Ruins ist kleiner oder gleich 0,5 %.
Das Kapital für die Berechnung des Risiko eines Ruins wurde auf 10 % der Ersteinlage oder 1000 Einheiten der Kontowährung festgelegt, wie im Abschnitt „Miscellaneous“ oben beschrieben.
Der beste Entwurf war (10,6,6), wie Sie in der ersten Zeile der Optimierungstabelle sehen können.
Beachten Sie, dass der Optimierer eine Lösung gefunden hat, die im Vergleich zum ersten Beispiel einen geringeren Gewinn aufweist (805,64 gegenüber 839,81), aber dieser neue Entwurf erfüllt alle fünf Nebenbedingungen und maximiert drei kombinierte Ziele.
Der GOF-Zusammenfassungsbericht
Durch Simulation der ersten Zeile in der obigen Optimierungstabelle erhalten wir den nachstehenden GOF-Zusammenfassungsbericht:
Die GOF-Zusammenfassung besteht aus drei Abschnitten. Der erste Abschnitt enthält viele Mengen aus der Registerkarte BackTest. Es gibt einige zusätzliche Größen, die auf der Registerkarte BackTest nicht vorhanden sind: Jährlicher Gewinn, Testdauer in Jahren, Reward/Risk Ratio (RRR), durchschnittliches und größtes Volumen, Gewinn- und Verlust-Standardabweichungen sowie die während der Simulation erreichte Mindestmarge.
Der zweite Abschnitt ist für die Zielfunktionen vorgesehen. Hier gibt es vier Werte für jedes Ziel: den Wert des Ziels, das Ziel, das Gewicht und den prozentualen Beitrag. Der prozentuale Beitrag ist der Beitrag dieser Zielfunktion zum gesamten Einzelziel. In diesem Beispiel trug die jährliche Rendite mit 95,1 %, der Erholungsfaktor mit 1,4 % und die Gewinnrate mit 3,5 % zu insgesamt 100 % zum Gesamtziel bei. Ziel und Gewichte beeinflussen diese Beiträge.
Der dritte Abschnitt ist für Nebenbedingungen vorgesehen. Für jede Nebenbedingung wird eine Meldung „pass“ (bestanden) oder „fail“ (nicht bestanden) gedruckt und ein Vergleich zwischen dem tatsächlichen Wert der Nebenbedingung und der gebundenen Eingabe wird ebenfalls angezeigt.
Zum Vergleich haben wir den ersten Entwurf aus Beispiel 1 (9, 9, 3) durch die gleiche Optimierungsformel wie in Beispiel 2 laufen lassen. Im Folgenden finden Sie die Zusammenfassung dieser Simulation. Beachten Sie, dass eine Nebenbedingung verletzt wird. Die Anzahl der aufeinanderfolgenden Verluste beträgt 6 und ist damit größer als der in der Optimierungsformulierung angegebene Grenzwert von 5. Obwohl der MaxBalance-Entwurf (9,9,3) einen besseren Gewinn als der MultiObjective/Constrained-Entwurf (10,6,6) aufweist, ist es der Entwurf (10,6,6), der alle Nebenbedingungen erfüllt.
Empfehlungen für die Verwendung von GenericOptimizationFormulation.mqh
Die größere Freiheit, mehrere Ziele und mehrere Nebenbedingungen zu wählen, sollte mit Bedacht genutzt werden. Hier sind einige allgemeine Empfehlungen:
- Verwenden Sie nicht mehr als 3 Ziele. Der Code lässt bis zu 5 Ziele zu, aber die Auswahl von Zielen und Gewichtungen, die das Endergebnis beeinflussen, wird schwieriger, wenn die Anzahl der Ziele mehr als 3 beträgt.
- Verwenden Sie bei der Auswahl der oberen (U_i) und unteren (L_i) Grenzen die von Ihnen bevorzugten Nebenbedingungen und legen Sie sie nicht zu eng an. Wenn die Grenzen zu eng gesteckt sind, erhalten Sie keine praktikable Kombination von Eingabevariablen.
- Wenn Sie nicht wissen, welchen Grenzwert Sie für eine bestimmte Nebenbedingung angeben sollen, können Sie die Nebenbedingung in den Zielbereich verschieben und ihr Verhalten (Größe, Vorzeichen usw.) anhand des GOF-Zusammenfassungsberichts überprüfen.
- Passen Sie k_o und k_p an, wenn Sie bessere Diagramme wünschen oder wenn Sie feststellen, dass der Optimierer nicht die erwarteten Ergebnisse liefert.
- Denken Sie daran, dass der optimale Entwurf (die oberste Zeile in der Optimierungstabelle) nicht unbedingt der rentabelste Entwurf ist, sondern der Entwurf mit dem höchsten Zielfunktionswert auf der Grundlage der von Ihnen gewählten individuellen Ziele und Nebenbedingungen.
- Wir empfehlen Ihnen, die Entwürfe nach der Optimierung nach anderen Spalten zu sortieren, z. B. nach Gewinn, Erholungsfaktor, Drawdown, erwarteter Auszahlung, Gewinnfaktor usw. Die oberste Zeile jeder Sortierung könnte ein Kandidat sein, den Sie simulieren könnten, um den GOF-Zusammenfassungsbericht zu überprüfen.
- Eine gute Auswahl von Zielen und Nebenbedingungen wurde in Beispiel 2 gezeigt. Verwenden Sie sie als Ausgangspunkt für Ihre Experimente.
Schiefgelaufene Dinge
Vielleicht haben Sie eine Optimierungsformel aufgestellt, die nicht das erwartete Ergebnis liefert. Hier sind einige Gründe, die dafür sprechen:
- Die Nebenbedingungen sind zu eng, und der genetische Optimierungsalgorithmus des MetaTrader 5 benötigt viele Generationen, um zu einer positiven Zielfunktion zu gelangen, und in einigen Fällen wird er dieses Ziel nicht erreichen. Lösung: Lockern Sie Ihre Zwänge.
- Zwänge stehen im Widerspruch zueinander. Lösung: Prüfen Sie, ob die Nebenbedingungen logisch konsistent sind.
- Die Darstellungen in der Optimierung haben eine Tendenz zu den negativen Werten auf der y-Achse (d. h. die negative Seite nimmt mehr Platz ein als die positive Seite). Lösung: K_o erhöhen oder K_p verringern, oder beides.
- Einige Designs, die Ihnen gefallen, erscheinen in der Optimierungstabelle nicht ganz oben. Beachten Sie, dass Ziele und Gewichtungen das Optimierungsziel beeinflussen und dass eine einzige Verletzung einer Nebenbedingung das Ziel in der Tabelle nach unten verschieben kann. Lösung: Formulieren Sie Ihr Optimierungsproblem neu, indem Sie die Ziele, Gewichte und Nebenbedingungen anpassen.
Schlussfolgerung
Wir hoffen, dass diese generische Optimierungsformulierung für Sie nützlich ist. Jetzt haben Sie die zusätzliche Freiheit, mehrere Ziele und mehrere Nebenbedingungen zu wählen, um das gewünschte Optimierungsproblem zu erstellen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/14365
![Datenwissenschaft und maschinelles Lernen (Teil 21): Neuronale Netze entschlüsseln, Optimierungsalgorithmen entmystifiziert](https://c.mql5.com/2/73/Data_Science_and_Machine_Learning_Part_21___LOGO.png)
![Nachrichtenhandel leicht gemacht (Teil 1): Erstellen einer Datenbank](https://c.mql5.com/2/74/News_Trading_Made_Easy_iPart_13_Creating_a_Database___LOGO__2.png)
![Einführung in MQL5 (Teil 6): Eine Anleitung für Anfänger zu den Array-Funktionen in MQL5 (II)](https://c.mql5.com/2/74/Introduction_to_MQL5_5Part_64_A_Beginneros_Guide_to_Array_Functions_in_MQL5____LOGO.png)
![MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 13): DBSCAN für eine Klasse für Expertensignale](https://c.mql5.com/2/73/MQL5_Wizard_Techniques_you_should_know_Part_13_DBSCAN_for_Expert_Signal_Class___LOGO.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.