Techniken des MQL5-Assistenten, die Sie kennen sollten (Teil 03): Shannonsche Entropie
Einführung
Claude Shannon veröffentlichte 1948 seinen Aufsatz „A mathematical theory of communication“ (Eine mathematische Theorie der Kommunikation), das eine neuartige Idee der Informationsentropie enthielt. Entropie ist ein Begriff aus der Physik. Sie ist ein Maß dafür, inwieweit Teilchen innerhalb eines Objekts aktiv sind. Betrachtet man beispielsweise die drei Zustände von Wasser, nämlich Eis, Flüssigkeit und Dampf, so stellt man fest, dass die kinetische Energie der Teilchen im Dampf am höchsten und im Eis am niedrigsten ist. Das gleiche Konzept wird in der Mathematik über die Wahrscheinlichkeit angewandt. Betrachten Sie die folgenden drei Sätze.
Satz 1:
Satz 2:
Satz 3:
Wenn Sie raten müssten, welche dieser Mengen weist die höchste Entropie auf?
Wenn Sie sich für die letzte Variante entschieden haben, haben Sie Recht, aber wie können wir diese Antwort überprüfen? Am einfachsten lässt sich diese Frage beantworten, indem man die Anzahl der Möglichkeiten, jede Menge neu zu organisieren, als Entropieschätzung verwendet und dabei ähnliche Farbabschnitte ignoriert. Für die erste Menge gibt es daher nur eine Möglichkeit, sie „neu zu ordnen“. Wenn wir uns jedoch die nachfolgenden Mengen ansehen, nimmt die Anzahl der Permutationen in Bezug auf die Farbe deutlich zu, so dass man argumentieren könnte, dass die letzte Menge die höchste Entropie aufweist.
Es gibt eine genauere Methode zur Bestimmung der Entropie, die auf Informationen basiert. Wenn jemand willkürlich einen Ball aus dem ersten Satz auswählen würde, bevor er diesen Ball auswählt, was würde er über ihn wissen? Da das Satz nur blaue Kugeln enthält, kann man sicher sein, dass es eine blaue Kugel ist. Man könnte also sagen, ich habe vollständige Informationen darüber, was ich aus Set 1 auswählen werde. Wenn wir die Sets 2 und 3 betrachten, werden unsere Informationen immer unvollständiger. In der Mathematik steht die Entropie daher in umgekehrtem Verhältnis zur Information. Je höher die Entropie ist, desto mehr Unbekannte gibt es.
Wie lässt sich nun eine Formel zur Berechnung der Entropie herleiten? Betrachtet man die Mengen noch einmal, so ist die Entropie umso höher, je größer die Vielfalt der Kugeltypen innerhalb einer Menge ist. Der empfindlichste Faktor bei der Messung der Informationen, die wir über eine Kugel vor ihrer Auswahl haben, könnte die Wahrscheinlichkeit der Auswahl jeder Kugel in einer Menge sein. Wenn wir also jeden Satz in seiner Gesamtheit betrachten und die Wahrscheinlichkeit der Auswahl von 8 Kugeln ermitteln, wobei jede Farbe so oft ausgewählt wird, wie sie in ihrem Satz vorkommt, da die Auswahl jeder Kugel ein unabhängiges Ereignis ist, dann ist die Wahrscheinlichkeit (und damit die „bekannte Information“) für jeden Satz das Produkt der individuellen Wahrscheinlichkeiten für jede Kugel.
Satz 1:
(1,0 x 1,0 x 1,0 x 1,0 x 1,0 x 1,0 x 1,0 x 1,0) = 1,0
Satz 2:
(0,625 x 0,625 x 0,625 x 0,625 x 0,625 x 0,375 x 0,375 x 0,375) ~ 0,0050
Satz 3:
(0,375 x 0,375 x 0,375 x 0,25 x 0,25 x 0,25 x 0,25 x 0,125) ~ 0,000025
Satz 1:
-(8 x 0,125 x log2(1,0)) = 0,0
Satz 2:
(-(0,625 x log2(0,625)) - (0,375 x log2(0,375))) ~ 0.9544
Satz 3:
(- (0,375 x log2(0,375)) - (2 x 0,25 x log2(0,25)) - (0,125 x log2(0,125))) ~ 1.906
2.0 Erstellen der Klasse
Für diesen Artikel werden wir die Klasse Decision Forest (‚Entscheidungswald‘) aus der MQL5-Bibliothek verwenden. Konkret werden wir die Idee des Random Forest (ein ‚Zufallswald‘ aus unkorrelierten Entscheidungsbäumen) abstrahieren, um die Wirksamkeit unseres Shannon-Entropie-Signals zu untersuchen. In diesem Artikel geht es nicht um Random Forest, sondern um die Shannonsche Entropie.
Betrachten wir noch einmal die Klasse von Random Forest, da sie die Grundlage für das Modell eines Random Forest ist. Das ist auch gut so, denn Entscheidungswälder sind ziemlich charakteristisch. Wir alle haben schon einmal wissentlich oder unwissentlich einen Entscheidungsbaum verwendet, und daher ist das Konzept an sich nicht ungewöhnlich.
Schauen wir uns ein Beispiel an, um zu verdeutlichen, wie eine solche Funktion funktioniert.
Angenommen, unser Datensatz besteht aus den oben genannten Preisbalken. Wir haben drei fallende Kerzen und fünf steigende Kerzen (wir nehmen bärische und bullische Märkte als unsere Klassen) und wollen die Klassen anhand ihrer Attribute trennen. Die Attribute sind die Kursrichtung und der Vergleich von Kopf- zu Schwanzlänge, da diese Vorläufer für Richtungsänderungen sein können. Wie können wir dies also tun?
Die Preisrichtung scheint ein einfaches Attribut zu sein, da weiß für bärische Kerzen und blau für bullische Kerzen steht. Wir können also die Frage „Fällt der Preis?“ verwenden, um die erste Entscheidung zu treffen. Ein Knotenpunkt in einem Baum ist der Punkt, an dem sich ein Zweig in zwei Teile teilt, die die Kriterien für die Auswahl des Ja- und des Nein-Zweigs erfüllen.
Beim Nein-Zweig (steigende Kerzen) sind die unteren Dochte länger als die oberen, sodass wir hier fertig sind, aber beim Ja-Zweig ist dies nicht der Fall, so dass noch mehr Arbeit zu tun ist. Wenn wir das zweite Attribut verwenden, fragen wir: „Ist obere Docht länger andere?“, um eine zweite Entscheidung zu treffen.
Die beiden Abwärtskerzen mit längeren oberen Dochten werden dem Ja-Zweig zugeordnet und eine Abwärtskerze mit einem längeren unteren Docht wird dem rechten Unterzweig zugeordnet. Dieser Entscheidungsbaum war in der Lage, die beiden Attribute zu verwenden, um die Daten perfekt nach unseren Kriterien aufzuteilen.
In echten Handelssystemen können Tick-Volumen, Tageszeit und eine Vielzahl anderer Handelsindikatoren zum Aufbau eines umfassenderen Entscheidungsbaums verwendet werden. Nichtsdestotrotz besteht die zentrale Prämisse jeder Frage an einem Knotenpunkt darin, das eine Attribut zu finden, das die obigen Daten in zwei unähnliche Mengen aufteilt (oder klassifiziert), wobei die erstellten Mitglieder jeder Menge ähnlich sind.
Der Random Forest besteht, wie der Name schon sagt, aus einer großen Anzahl von einzelnen Entscheidungsbäumen, die als Gruppe arbeiten. Für jeden Baum des Random Forest wird eine Klassenvorhersage gemacht, und die Klasse mit den meisten Stimmen wird als Vorhersage des Modells ausgewählt. Das Hauptkonzept hinter Random Forest ist leicht zu übersehen, aber es ist sehr wirkungsvoll - die Weisheit der Masse. Bei der Erstellung von Vorhersagen sind unkorrelierte Bäume den einzelnen Bäumen überlegen, unabhängig davon, auf wie vielen Daten die einzelnen Bäume trainiert wurden. Die Krux ist eine geringe Korrelation. Der Grund dafür ist, dass sich die Bäume nach Ton gegenseitig vor ihren individuellen Fehlern schützen (solange sie nicht ständig alle in dieselbe Richtung irren). Damit ein Random Forest funktioniert, muss das Vorhersagesignal besser als der Durchschnitt sein und die Fehler der einzelnen Bäume müssen eine geringe Korrelation zueinander aufweisen.
Wenn Sie zum Beispiel zwei Handelssysteme hätten, bei denen Sie im ersten bis zu eintausend Ein-Dollar-Marge-Aufträge in einem Jahr platzieren können und im anderen nur einen 1000-Dollar-Marge-Auftrag, welches würden Sie vorziehen, wenn Sie eine ähnliche Erwartung hätten? Für die meisten Menschen ist es das erste System, da es dem Händler „mehr Kontrolle“ gibt.
Auf welche Weise stellen Random-Forest-Algorithmen also sicher, dass das Merkmal jedes einzelnen Baumes nicht zu stark mit dem Merkmal eines der anderen Bäume im Wald korreliert? Es könnte auf zwei Merkmale hinauslaufen:
Bagging
Entscheidungsbäume reagieren sehr empfindlich auf Trainingsdaten, und kleine Änderungen können zu deutlich unterschiedlichen Random Forest führen. Random Forest macht sich dies zunutze, indem jeder Baum während des Ersetzens zufällige Stichproben aus dem Datensatz zieht, was zu unterschiedlichen Bäumen führt. Dieser Prozess wird als Bagging (‚einsacken‘) bezeichnet, eine Abkürzung für Bootstrap-Aggregation.
Beachten Sie, dass wir beim Bagging die Trainingsdaten nicht in kleinere Sätze aufteilen, sondern anstelle der ursprünglichen Trainingsdaten eine Zufallsstichprobe der Größe N mit einigen Ersetzungen nehmen. Die anfängliche Mengengröße N wird beibehalten. Wenn unsere Trainingsdaten zum Beispiel [U, V, W, X, Y, Z] lauten, könnten wir einem unserer Bäume die folgende Liste [U, U, V, X, X, Z] geben. In beiden Listen wird die Größe N (sechs) beibehalten, und sowohl „U“ als auch „X“ werden in den zufällig ausgewählten Daten wiederholt.
2.0.2 Das Merkmal der Zufälligkeit
In einem Entscheidungsbaum werden bei der Aufteilung des Knotens in der Regel alle möglichen Merkmale berücksichtigt und dasjenige ausgewählt, das den größten Unterschied zwischen den Beobachtungen im linken und im rechten Knoten ergibt. Bei einem Zufallsforst hingegen kann jeder Baum nur aus einer zufälligen Teilmenge von Merkmalen auswählen. Dies führt zu einer größeren Variation innerhalb des Waldes und letztlich zu einer geringeren Korrelation zwischen den Bäumen und einer größeren Diversifizierung.
Gehen wir ein visuelles Beispiel durch - in der obigen Abbildung kann der herkömmliche Entscheidungsbaum (in blau) aus allen vier Merkmalen auswählen, wenn er entscheidet, wie der Knoten zu teilen ist. Er entscheidet sich für Merkmal 1 (schwarz und unterstrichen), da es die Daten in möglichst getrennte Gruppen aufteilt.
Werfen wir nun einen Blick auf unseren Random Forest. Wir werden in diesem Beispiel nur zwei Bäume des Waldes untersuchen. Wenn wir den Random Forest Baum 1 überprüfen, stellen wir fest, dass er nur die (zufällig ausgewählten) Merkmale 2 und 3 für seine Knotenaufteilungsentscheidung berücksichtigen kann. Aus unserem traditionellen Entscheidungsbaum (in blau) wissen wir, dass Merkmal 1 das beste Merkmal für die Aufteilung ist, aber Baum 1 kann Merkmal 1 nicht sehen, sodass er gezwungen ist, sich für Merkmal 2 (schwarz und unterstrichen) zu entscheiden. Baum 2 hingegen kann nur die Merkmale 1 und 3 sehen und ist daher in der Lage, das Merkmal 1 auszuwählen.
In einem Random Forest werden Bäume nicht nur auf Datensätzen trainiert (dank Bagging), sondern verwenden auch eine Vielzahl von Merkmalen, um Entscheidungen zu treffen.
Bei der Verwendung von Random Forests verarbeiten unsere Experten vergangene Handelsergebnisse und Marktsignale, um eine Kauf- oder Verkaufsentscheidung zu treffen. Um ein Signal und nicht nur einen Test der Entropie zu erhalten, werden wir die Entropie positiver Preisbalken und die Entropie negativer Preisbalken innerhalb eines aktuellen Sets getrennt betrachten.
Die Erstellung und das Training von Zufallswäldern erfolgt nur bei der Optimierung mit einem einzigen Thread.
2.1 Signalklasse für einen Experten
// wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Signals of'Shannon Entropy' | //| Type=SignalAdvanced | //| Name=Shannon Entropy | //| ShortName=SE | //| Class=CSignalSE | //| Page=signal_se | //| Parameter=Reset,bool,false,Reset Training | //| Parameter=Trees,int,50,Trees number | //| Parameter=Regularization,double,0.15,Regularization Threshold | //| Parameter=Trainings,int,21,Trainings number | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| Class CSignalSE. | //| Purpose: Class of generator of trade signals based on | //| the 'Shannon Entropy' signals. | //| Is derived from the CExpertSignal class. | //+------------------------------------------------------------------+ class CSignalSE : public CExpertSignal { public: //Decision Forest objects. CDecisionForest DF; //Decision Forest CMatrixDouble DF_SIGNAL; //Decision Forest Matrix for inputs and output CDFReport DF_REPORT; //Decision Forest Report for results int DF_INFO; //Decision Forest feedback double m_out_calculations[2], m_in_calculations[__INPUTS]; //Decision Forest calculation arrays //--- adjusted parameters bool m_reset; int m_trees; double m_regularization; int m_trainings; //--- methods of setting adjustable parameters void Reset(bool value){ m_reset=value; } void Trees(int value){ m_trees=value; } void Regularization(double value){ m_regularization=value; } void Trainings(int value){ m_trainings=value; } //Decision Forest FUZZY system objects CMamdaniFuzzySystem *m_fuzzy; CFuzzyVariable *m_in_variables[__INPUTS]; CFuzzyVariable *m_out_variable; CDictionary_Obj_Double *m_in_text[__INPUTS]; CDictionary_Obj_Double *m_out_text; CMamdaniFuzzyRule *m_rule[__RULES]; CList *m_in_list; double m_signals[][__INPUTS]; CNormalMembershipFunction *m_update; datetime m_last_time; double m_last_signal; double m_last_condition; CSignalSE(void); ~CSignalSE(void); //--- method of verification of settings virtual bool ValidationSettings(void); //--- method of creating the indicator and timeseries virtual bool InitIndicators(CIndicators *indicators); //--- methods of checking if the market models are formed virtual int LongCondition(void); virtual int ShortCondition(void); bool m_random; bool m_read_forest; int m_samples; //--- method of initialization of the oscillator bool InitSE(CIndicators *indicators); double Data(int Index){ return(Close(StartIndex()+Index)-Close(StartIndex()+Index+1)); } void ReadForest(); void WriteForest(); void SignalUpdate(double Signal); void ResultUpdate(double Result); double Signal(void); double Result(void); bool IsNewBar(void); };
2.1.1 Signale
Diese Entropie wird mit dem Index für die Aktualität gewichtet.
if(_data>0.0) { _long_entropy-=((1.0/__SIGNALS[i])*((__SIGNALS[i]-s)/__SIGNALS[i])*(fabs(_data)/_range)*(log10(1.0/__SIGNALS[i])/log10(2.0))); } else if(_data<0.0) { _short_entropy-=((1.0/__SIGNALS[i])*((__SIGNALS[i]-s)/__SIGNALS[i])*(fabs(_data)/_range)*(log10(1.0/__SIGNALS[i])/log10(2.0))); }
Und sie wird auch nach der Größe der Preisbalken gewichtet werden.
if(_data>0.0) { _long_entropy-=((1.0/__SIGNALS[i])*((__SIGNALS[i]-s)/__SIGNALS[i])*(fabs(_data)/_range)*(log10(1.0/__SIGNALS[i])/log10(2.0))); } else if(_data<0.0) { _short_entropy-=((1.0/__SIGNALS[i])*((__SIGNALS[i]-s)/__SIGNALS[i])*(fabs(_data)/_range)*(log10(1.0/__SIGNALS[i])/log10(2.0))); }
Das erzeugte Signal ist die Entropie der negativen Balken minus die Entropie der positiven Balken. Wenn die Entropie der negativen Balken die der positiven Balken übersteigt, haben wir weniger Informationen über die negativen Balken und damit eine Verkaufsposition als über die positiven Balken, die ebenfalls eine Kaufposition bedeuten. Oberflächlich betrachtet scheint es sich um ein „gefährliches“ Trendfolgesystem zu handeln! Aufgrund der Gewichtungen, die oben bei der Berechnung der Entropie vorgenommen wurden, ist dies jedoch nicht unbedingt der Fall.
Die Signale werden aktualisiert, wenn die Kauf- und Verkaufs-Bedingung den Schwellenwert für die Eröffnung überschreitet, da dies bedeutet, dass eine Position eröffnet wird. Dies wird mit einer Zeitschaltuhr geschehen, sodass wir den Assistenten, der den Experten zusammenstellt, entsprechend anpassen werden.
//+------------------------------------------------------------------+ //| "Timer" event handler function | //+------------------------------------------------------------------+ void OnTimer() { if(PositionSelect(Symbol()) && Signal_ThresholdClose<=fabs(filter0.m_last_condition)) { filter0.ResultUpdate(filter0.Result()); } // if(!PositionSelect(Symbol()) && Signal_ThresholdOpen<=fabs(filter0.m_last_condition)) { filter0.SignalUpdate(filter0.m_last_signal); } ExtExpert.OnTimer(); }
Die Quellfunktion in der Klasse wird, wie bereits erwähnt, nur bei der Optimierung ausgeführt.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSignalSE::SignalUpdate(double Signal) { if(MQLInfoInteger(MQL_OPTIMIZATION)) { m_samples++; DF_SIGNAL.Resize(m_samples,__INPUTS+2); for(int i=0;i<__INPUTS;i++) { DF_SIGNAL[m_samples-1].Set(i,m_signals[0][i]); } // DF_SIGNAL[m_samples-1].Set(__INPUTS,Signal); DF_SIGNAL[m_samples-1].Set(__INPUTS+1,1-Signal); } }
2.1.2 Ergebnisse
Die Ergebnisse basieren auf dem Gewinn der letzten geschlossenen Position.
if(HistorySelect(0,m_symbol.Time())) { int _deals=HistoryDealsTotal(); for(int d=_deals-1;d>=0;d--) { ulong _deal_ticket=HistoryDealGetTicket(d); if(HistoryDealSelect(_deal_ticket)) { if(HistoryDealGetInteger(_deal_ticket,DEAL_ENTRY)==DEAL_ENTRY_OUT) { _result=HistoryDealGetDouble(_deal_ticket,DEAL_PROFIT); break; } } } } return(_result);
Und sie werden aktualisiert, wenn die Kauf- und Verkaufs-Bedingung den Schwellenwert für das Schließen überschreitet, da dies bedeutet, dass eine Position geschlossen wird. Auch dies geschieht, wie oben beschrieben, über einen Zeitgeber, wobei die Klassenfunktion die Entscheidungswalddateien nur bei der Optimierung aktualisiert.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CSignalSE::ResultUpdate(double Result) { if(MQLInfoInteger(MQL_OPTIMIZATION)) { int _err; if(Result<0.0) { double _odds = MathRandomUniform(0,1,_err); // DF_SIGNAL[m_samples-1].Set(__INPUTS,_odds); DF_SIGNAL[m_samples-1].Set(__INPUTS+1,1-_odds); } } }
2.1.3 Erstellen eines Forest
Der Wald wird bei jedem Tick geschrieben, bevor er gelesen wird, also ändern wir den Assistenten, der den Experten zusammenstellt, um dies zu berücksichtigen.
//+------------------------------------------------------------------+ //| "Tick" event handler function | //+------------------------------------------------------------------+ void OnTick() { if(!signal_se.m_read_forest) signal_se.WriteForest(); ExtExpert.OnTick(); }
2.1.4 Lesen des Forest
Der Wald wird am Ende eines Testerdurchlaufs vom Tester gelesen, daher ändern wir den Assistenten, der den Experten zusammenstellt, um dies zu berücksichtigen.
//+------------------------------------------------------------------+ //| "Tester" event handler function | //+------------------------------------------------------------------+ double OnTester() { signal_se.ReadForest(); return(0.0); }
2.2 Die Klasse Expert Money
In diesem Artikel befassen wir uns auch mit der Erstellung einer nutzerdefinierten Positionsgrößenklasse, die mit dem Assistenten verwendet werden kann. Wir nehmen die Klasse „Money Size Optimized“ und ändern sie, um die Positionsgrößen auf der Grundlage der Shannon-Entropie zu normalisieren. Unsere neue Schnittstelle wird wie folgt aussehen:
// wizard description start //+------------------------------------------------------------------+ //| Description of the class | //| Title=Trading with 'Shannon Entropy' optimized trade volume | //| Type=Money | //| Name=SE | //| Class=CMoneySE | //| Page=money_se | //| Parameter=ScaleFactor,int,3,Scale factor | //| Parameter=Percent,double,10.0,Percent | //+------------------------------------------------------------------+ // wizard description end //+------------------------------------------------------------------+ //| Class CMoneySE. | //| Purpose: Class of money management with 'Shannon Entropy' optimized volume. | //| Derives from class CExpertMoney. | //+------------------------------------------------------------------+ class CMoneySE : public CExpertMoney { protected: int m_scale_factor; public: double m_absolute_condition; CMoneySE(void); ~CMoneySE(void); //--- void ScaleFactor(int scale_factor) { m_scale_factor=scale_factor; } void AbsoluteCondition(double absolute_condition) { m_absolute_condition=absolute_condition; } virtual bool ValidationSettings(void); //--- virtual double CheckOpenLong(double price,double sl); virtual double CheckOpenShort(double price,double sl); protected: double Optimize(double lots); };
Die Variable „m_absolute_condition“ ist der absolute Wert der Ganzzahl(en), die von den Funktionen „LongCondition“ und „ShortCondition“ zurückgegeben werden. Da es sich um einen normalisierten Wert handelt, können wir seine Größe zur Proportionierung unserer Positionsgröße verwenden. Diese Variable wird von der Signalklasse an die Geldklasse weitergegeben, und zwar über Änderungen am vom Assistenten zusammengestellten Experten.
//+------------------------------------------------------------------+ //| Global expert object | //+------------------------------------------------------------------+ CExpert ExtExpert; CSignalSE *signal_se; CMoneySE *money_se;
Und in der Funktion OnTick()
//+------------------------------------------------------------------+ //| "Tick" event handler function | //+------------------------------------------------------------------+ void OnTick() { if(!signal_se.m_read_forest) signal_se.WriteForest(); money_se.AbsoluteCondition(fabs(signal_se.m_last_condition)); ExtExpert.OnTick(); }
Die wichtigsten Änderungen werden in der Funktion „Optimize“ unten vorgenommen:
//+------------------------------------------------------------------+ //| Optimizing lot size for open. | //+------------------------------------------------------------------+ double CMoneySE::Optimize(double lots) { double lot=lots; //--- normalize lot size based on magnitude of condition lot*=(20*m_scale_factor/fmax(20.0,((100.0-m_absolute_condition)/100.0)*20.0*m_scale_factor*m_scale_factor)); //--- reduce lot based on number of losses orders without a break if(m_scale_factor>0) { //--- select history for access HistorySelect(0,TimeCurrent()); //--- int orders=HistoryDealsTotal(); // total history deals int losses=0; // number of consequent losing orders CDealInfo deal; //--- for(int i=orders-1;i>=0;i--) { deal.Ticket(HistoryDealGetTicket(i)); if(deal.Ticket()==0) { Print("CMoneySE::Optimize: HistoryDealGetTicket failed, no trade history"); break; } //--- check symbol if(deal.Symbol()!=m_symbol.Name()) continue; //--- check profit double profit=deal.Profit(); if(profit>0.0) break; if(profit<0.0) losses++; } //--- if(losses>1){ lot*=m_scale_factor; lot/=(losses+m_scale_factor); lot=NormalizeDouble(lot,2);} } //--- normalize and check limits double stepvol=m_symbol.LotsStep(); lot=stepvol*NormalizeDouble(lot/stepvol,0); //--- double minvol=m_symbol.LotsMin(); if(lot<minvol){ lot=minvol; } //--- double maxvol=m_symbol.LotsMax(); if(lot>maxvol){ lot=maxvol; } //--- return(lot); }
MQL5-Assistent
Wir werden zwei Expert Advisors zusammenstellen, einen nur mit der von uns erstellten Signalklasse und einem Mindesthandelsvolumen für die Geldverwaltung, und den zweiten mit der von uns erstellten Signal- und Geldverwaltungsklasse.
4.0 Strategietester
Die Optimierung für den ersten Experten ergibt einen Gewinnfaktor von 2,89 und eine Sharpe Ratio von 4,87 gegenüber einem Gewinnfaktor von 3,65 und einer Sharpe Ratio von 5,79 bei der Optimierung des zweiten Experten.
Erster Bericht
Erste Kapitalkurve
Zweiter Bericht
Zweite Kapitalkurve
5.0 Schlussfolgerung
Die beigefügten Experten wurden auf Basis von Eröffnungskursen des 4-Stunden-Zeitrahmens für ideale Take-Profit- und Stop-Loss-Levels optimiert, d.h. Sie können diese Ergebnisse nicht auf einem Live-Konto oder sogar im Strategietester in jedem Tick-Modus replizieren. Darum ging es in dem Artikel nicht. Anstatt zu versuchen, einen Gral zu beschreiben, den jeder wiederholen könnte, soll diese Reihe von Artikeln versuchen, verschiedene Ideen, die weiter angepasst werden müssten, sodass damit jeder Händler sein eigenes Glück versuchen kann. Die Märkte sind insgesamt noch zu stark korreliert, weshalb die Suche nach hoher Volatilität von Vorteil sein kann. Danke fürs Lesen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/11487
- 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.