![Prinzipien der wirtschaftlichen Berechnung von Indikatoren](https://c.mql5.com/i/articles/overlay.png)
Prinzipien der wirtschaftlichen Berechnung von Indikatoren
Einleitung
Das Thema der Einsparung von Ressourcen in allen beliebigen Teilbereichen der Tätigkeiten der Menschen ist vielleicht eines der wichtigsten und dringendsten Themen für die weitere Entwicklung und den Fortschritt. Die Programmierung in MQL5 ist davon nicht ausgenommen. Wenn der Aufgabenbereich nur auf den visuellen Handel beschränkt ist, können viele Schwachstellen der Programmierung natürlich unbemerkt bleiben.
Doch alles, was mit automatisiertem Handel zusammenhängt, erfordert zunächst eine möglichst sparsame Schreibweise des Codes. Andernfalls können das Testing und die Optimierung von Handelsrobotern so lange andauern, dass es fast unmöglich wäre, auf ihre Fertigstellung zu warten. Die Vorstellung, in einer solchen Situation etwas von Wert zu erschaffen, ist mehr als zweifelhaft.
Bevor man also mit der Umsetzung von Handelsstrategien beginnt, ist es sinnvoll, sich mit Details der Programmierung vertraut zu machen, die sich auf die Optimierungs- und Testzeit von Expert Advisors auswirken. Und da ein Großteil der Expert Advisors Aufrufe der Benutzerindikatoren in seinen Codes beinhaltet, schlage ich vor, fangen wir auch damit an.
Alles in allem gibt es nicht allzu viele relevante Punkte, die man beim Erstellen von Indikatoren im Kopf behalten muss, also erscheint es logisch, sie einfach einen nach dem anderen anzusehen.
Neuberechnung noch nicht berechneter, neu erschienener Balken in klassischen Indikatoren bei jedem Indikator-Tick
Die Essenz klassischer Indikatoren wie RSI, ADX, ATR, CCI usw. ist, dass die Berechnung dieser Indikatoren für geschlossene Balken nur einmal durchgeführt werden kann und anschließend nur eine Berechnung neu erscheinender Balken möglich ist. Die einzige Ausnahme bildet der aktuell offene Balken, für den bei jedem Tick eine Berechnung stattfindet, bis der Balken geschlossen wird.
Die einfachste Möglichkeit, herauszufinden, ob es sinnvoll wäre, die Indikatoren von nicht berechneten Balken zu berechnen, ist der Vergleich der Ergebnisse der Ausführung solcher (optimierter) Indikatoren mit (nicht optimierten) Indikatoren, die ständig für alle Balken neu berechnet werden, im Strategietester.
Das ist ziemlich einfach. Ein Expert Advisor wird mit den leeren Funktionen OnInit() und OnTick() erstellt. Sie müssen lediglich den Aufruf in den Expert Advisor der benötigten Version eines optimierten oder nicht optimierten Indikators schreiben und können die Ergebnisse von Durchläufen solcher Experts im Tester für beide Fälle bewundern. Als Beispiel nehme ich den Indikator SMA.mq5 aus meinem Beitrag "Benutzerdefinierte Indikatoren in MQL5 für Anfänger", in dem ich Zeilen ersetze.
if (prev_calculated == 0) // if this is the first start, then make a recalculation of all existing bars first = MAPeriod - 1 + begin; else first = prev_calculated - 1; // on all subsequent starts, make calculations only on newly appeared bars
durch
first = MAPeriod - 1 + Begin; / / On all ticks the bars are being recalculated
Als Ergebnis erhalte ich eine nicht optimierte Version des Programmiercodes (SMA!!!!!!.mq5), die im Gegensatz zum Original all ihre Werte bei jedem Tick neu berechnet. Grundsätzlich sind beide Versionen des Expert-Codes praktisch identisch, weshalb ich nur eine davon zur Verfügung stelle (SMA_Test.mq5).
//+------------------------------------------------------------------+ //| SMA_Test.mq5 | //| Copyright 2010, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2010, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" int Handle; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //----+ //----+ Obtaining the indicator handle Handle = iCustom(Symbol(), 0, "SMA"); if (Handle == INVALID_HANDLE) Print(" Not able to obtain the indicator handle SMA"); //----+ return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //----+ //--- Release the indicator handle IndicatorRelease(Handle); //----+ } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //----+ double SMA[1]; //----+ Using the indicator handle, copy the values of the indicator // buffer in a specially prepared for this statistical array CopyBuffer(Handle, 0, 0, 1, SMA); //----+ } //+------------------------------------------------------------------+
Wir können nun mit den Tests beginnen. Es muss beachtet werden, dass wir in allen im Zuge dieses Beitrags durchgeführten Tests einen Simulationsmodus von Balkenänderungen nutzen, der so nah wie möglich an der Realität ist – "Every tick" (Jeder Tick)!
Hier sehen Sie das Ergebnis der Ausführung eines optimierten Indikators im Tester:
Die rote Markierung kennzeichnet die Zeit, die für den Test benötigt wurde. Das ist gar nicht so viel! Auf den Abschluss des Tests des Indikators SMA!!!!!!.mq5 mussten wir allerdings sehr lange warten!
In der Tat beträgt die Verarbeitungszeit der Tests in diesem Fall mehr als das 500-Fache des vorherigen Tests. Und das, obwohl ein ausreichend kurzer Testzeitraum ausgewählt wurde. Doch während wir uns beim Testen der Expert Advisors mit so hohen Berechnungszeiten anfreunden können, können wir die Optimierung ihrer Parameter komplett vergessen!
Somit ist dies der beste Beweis dafür, dass ein sparsam geschriebener Code nicht nur ein Zeitvertreib für Programmierexperten ist, sondern ein ziemlich logischer Ansatz zum Schreiben Ihres eigenen Codes.
Mit Overclockers.ru widmet sich eine ganze Webseite der Beschleunigung von PCs, um ihre Leistung zu maximieren. Das wichtigste Mittel dieser Vorgehensweise ist die Verwendung viel teurerer Computerkomponenten zur Steigerung der Taktrate des Prozessors und des Arbeitsspeichers.
Ist das erledigt, wird der übertaktete Prozessor mit teureren Wasserkühlsystemen ausgestattet oder sogar in Flüssigstickstoff getaucht. Das Ergebnis ist eine Verdoppelung oder sogar Verdreifachung der Leistung des PCs.
Mit einer fachgemäßen, sparsamen Schreibweise des Codes können wir mit wenig Einsatz viel erreichen. Natürlich kann diese Methode keinen Celeron300A in einen Core 2 Quad Q6600 verwandeln, doch sie macht es möglich, dass ein herkömmlicher Standard-PC so gut arbeitet, wie man es von einem High-End-Modell erwarten würde!
Wiederholte Neuberechnung des aktuellen nicht geschlossenen Balkens in einigen nicht ganz klassischen Indikatoren
Es wäre einfach toll, wenn diese Methode der Optimierung von Programmiercodes für alle Indikatoren gleichermaßen geeignet wäre! Doch leider ist sie das nicht. Es gibt eine ganze Reihe von Indikatoren, die bei dieser Herangehensweise beginnen, während des Ladens des Indikators für bereits vorhandene historische Daten ganz normal einfach zu berechnen.
Ihre Werte erweisen sich für alle Balken, die nach dem Laden des Indikators erschienen sind, als völlig falsch. Der Hauptgrund dafür ist, dass bestimmte Variablen des Indikatorcodes von Indikatoren abhängen, die nach der Berechnung des Indikators des vorherigen Balkens die gleichen Variablen hatten. Formell sieht das so aus:
SomeVariable(bar) = Function(SomeVariable(bar - 1))
mit:
- SomeVariable() – der Wert einer bestimmten Variable für einen bestimmten Balken;
- bar – Nummer des Balkens, für den die Berechnung durchgeführt wird.
Aus offensichtlichen Gründen haben solche Abhängigkeiten im tatsächlichen Code eine weniger eindeutige funktionale Form. Doch die Essenz ändert sich nicht. Für den Moving von T3 (nicht optimierter Indikator T3!!!!!!.mq5) sieht der für uns relevante Teil des Codes so aus:
e1 = w1 * series + w2 * e1;
e2 = w1 * e1 + w2 * e2;
e3 = w1 * e2 + w2 * e3;
e4 = w1 * e3 + w2 * e4;
e5 = w1 * e4 + w2 * e5;
e6 = w1 * e5 + w2 * e6;
//----
T3 = c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3;
Die Variablen e1, e2, e3, e4, e5, e6 haben eine funktionale Abhängigkeit, die die Verwendung dieses Codes für die einmalige Berechnung jedes neuen Balkens ermöglicht! Doch der aktuelle Balken wird durch eine ähnliche Berechnung wiederholt übersprungen, bis er geschlossen wird.
Die Werte dieser Variablen auf dem aktuellen Balken ändern sich ständig, obwohl sie für den aktuellen Balken, bevor er geändert wird, bleiben sollten, wie sie nach der Berechnung des vorherigen Balkens waren!
Deshalb sollten die Werte dieser Variablen auf dem vorherigen Balken (in Bezug auf den aktuellen Balken) in den statischen Variablen gespeichert und dann bis zur nächsten Änderung des Balkens wiederverwendet werden, auf dem die vorletzten Werte der Variablen e1, e2, e3, e4, e5, e6 erneut gespeichert werden sollten.
Der zusätzliche Code, der ähnliche Manipulationen an den Werten vornimmt, ist ziemlich simpel. Als Erstes müssen Sie die lokalen statischen Variablen zum Speichern von Werten innerhalb der Funktion OnCalculate() deklarieren.
//---- declare the static variables for storing the valid values of the coefficients static double e1_, e2_, e3_, e4_, e5_, e6_;
Anschließend führen Sie die Speicherung der Werte der Variablen im Zyklus auf dem aktuellen Balken zu dem Zeitpunkt, zu dem die Menge neu erschienener Balken größer als Null ist, vor etwaigen Berechnungen durch:
//---- memorize the values of variables before the run on the current bar if (rates_total != prev_calculated && bar == rates_total - 1) { e1_ = e1; e2_ = e2; e3_ = e3; e4_ = e4; e5_ = e5; e6_ = e6; }
Vor dem Block des Zyklus-Operators stellen sie die Werte der Variablen durch umgekehrte Transformation wieder her:
//---- restore the values of the variables
e1 = e1_;
e2 = e2_;
e3 = e3_;
e4 = e4_;
e5 = e5_;
e6 = e6_;
Natürlich wird die Start-Initialisierung der Berechnungskoeffizienten jetzt nur einmal durchgeführt, beim ersten Start der Funktion OnCalculate(), und es werden nicht mehr die Koeffizienten selbst initialisiert, sondern die entsprechenden statischen Variablen.
//---- calculating the starting number first for the cycle of recalculation of bars if (prev_calculated == 0) // verification of the first start of the indicator calculation { first = begin; // the starting number for calculating all of the bars //---- the starting initialization of calculated coefficients e1_ = price[first]; e2_ = price[first]; e3_ = price[first]; e4_ = price[first]; e5_ = price[first]; e6_ = price[first]; }
Als Ergebnis fing der finale Indikator T3.mq5 an, Berechnungen auf die sparsamste Weise durchzuführen. Das ist gar nicht schlecht, aber ähnliche funktionale Abhängigkeiten können nicht immer so einfach entdeckt werden. In diesem Fall können die Werte aller Indikatorvariablen in statischen Variablen gespeichert und auf die gleiche Weise wiederhergestellt werden.
Erst danach können wir anfangen, uns zu überlegen, welche Variablen tatsächlich wiederhergestellt werden müssen. Dazu müssen wir die nicht optimierte und optimierte Version des Indikators an das Diagramm anhängen und ihre Funktion prüfen, während wir eine Variable nach der anderen von der Wiederherstellungsliste entfernen. Am Ende bleiben nur diejenigen Variablen, die wirklich wiederhergestellt werden müssen.
Natürlich habe ich diese Version der Arbeitslogik mit dem Programmcode der normalen Indikatoren zur Verfügung gestellt, in dem eine Neuzählung des aktuellen Balkens und der neu erscheinenden Balken stattfindet. Für Indikatoren, die sich umschreiben und in die Zukunft blicken, können wir aufgrund ihrer einzigartigen Merkmale keine analoge, standardgemäße und simple Methode zur Code-Optimierung erschaffen. Die Mehrheit der erfahrenen Entwickler von Expert Advisors sieht auch keinen Grund dazu. Deshalb können wir an dieser Stelle die detaillierte Analyse dieser Indikatoren als abgeschlossen betrachten.
Merkmale von Indikatoraufrufen, die den MQL5-Code übermäßig langsam machen können
Unsere Aufgabe scheint abgeschlossen: Wir haben einen optimierten Indikator, der Balken auf die sparsamste Weise zählt, und nun müsste es reichen, ein paar Codezeilen zu schreiben und diesen Indikator im Code des Expert Advisors oder Indikators aufzurufen, um die berechneten Werte aus dem Indikatorpuffer zu erhalten.
Doch das ist nicht so einfach, wie es mit einer rein formalen Herangehensweise und ohne Berücksichtigung der hinter diesen wenigen Codezeilen steckenden Vorgänge erscheinen würde.
Das Besondere am Abrufen der Werte aus den benutzerdefinierten und technischen Indikatoren ist, genauso wie bei der Zeitserie in MQL5, dass dies durch das Kopieren von Daten in die benutzerdefinierten Variablen-Arrays geschieht. Das kann zu einer Ansammlung von Daten führen, die für die aktuellen Berechnungen völlig unnötig sind.
Dies lässt sich am einfachsten anhand eines konkreten Datenempfangs aus einem technischen Indikator überprüfen. Beispielsweise können wir den Moving iAMA nehmen und auf Basis dieses technischen Indikators den benutzerdefinierten Indikator AMkA erstellen.
Zum Kopieren der Daten nutzen wir die erste Version des Aufrufs der Funktion CopyBuffer() mit einer Anfrage an die Startposition und die Menge der zum Kopieren erforderlichen Elemente. Im AMkA-Indikator wird die Inkrementierung des Moving auf dem aktuellen Balken mithilfe des technischen Indikators Standard Deviation verarbeitet und anschließend wird dieses Inkrement zum Abrufen der Handelssignale mit dem insgesamt verarbeiteten Wert der Standardabweichung verglichen.
Deshalb sollten Sie im einfachsten Fall der Implementierung des AMkA-Indikators zuerst einen Indikator erstellen, in dem der Indikatorpuffer den Wert der Moving-Inkrementierung (Indikator dAMA) enthält. Anschließend erhalten wir im anderen Indikator mithilfe des Indikator-Handles mit AMA-Inkrementen den endgültigen Wert, der durch den Indikator Standard Deviation verarbeitet wird.
Der Prozess zur Erstellung vergleichbarer Indikatoren wurde in diversen Beiträgen zu diesem Thema bereits detailliert behandelt, deshalb halte ich mich nicht damit auf und analysiere nur die Details des Zugriffs auf die Indikatorpuffer des aufgerufenen Indikatorpuffers im Code eines anderen Indikators.
In den Weiten des Internets können wir bereits den Aufstieg von MQL5-Beispielen beobachten, in denen die Autoren buchstäblich die gesamten Inhalte der Indikatorpuffer in die dynamischen Zwischen-Arrays kopieren. Anschließend werden alle Werte einer nach dem anderen mithilfe des Zyklus-Operators aus diesen zwischen-Arrays in die finalen Indikatorpuffer übertragen.
Für die Lösung unseres Problems erscheint diese Herangehensweise so einfach wie möglich.
if (CopyBuffer(AMA_Handle, 0, 0, rates_total, Array) <= 0) return(0); ArrayCopy(AMA_Buffer, Array, 0, 0, WHOLE_ARRAY);
(Indikator dAMA!!!!!!.mq5) Oder so:
if (CopyBuffer(AMA_Handle, 0, 0, rates_total, Array) <= 0) return(0); for(bar = 0; bar < rates_total; bar++) { AMA_Buffer[bar] = Array[bar]; /* here is the code of indicator calculations */ }
Doch was ist der Preis einer so schlichten Lösung? Zuerst wäre es nicht schlecht, zu verstehen, was die rationalste Herangehensweise wäre. Erstens gibt es keinen nachvollziehbaren Grund für die Verwendung des Zwischen-Arrays Array[] und die Daten sollten direkt in den Indikatorpuffer AMA[] kopiert werden.
Zweitens müssen die Werte in nur drei Fällen bei jedem Indikator-Tick kopiert werden:
- bei neu erschienenen Balken,
- bei geschlossenen Balken,
- bei aktuell offenen Balken.
Die verbleibenden Werte im Indikatorpuffer existieren bereits und es ist sinnlos, sie mehrfach neu zu schreiben.
//--- calculation of the required number of copied data int to_copy; if(prev_calculated > rates_total || prev_calculated <= 0)// verification for the first start of indicator calculation to_copy = rates_total - begin; // calculated number of all bars else to_copy = rates_total - prev_calculated + 1; // calculated number of only new bars //--- copy the reappearing data into the indicator buffer AMA_Buffer[] if (CopyBuffer(AMA_Handle, 0, 0, to_copy, AMA_Buffer) <= 0) return(0);
Selbstverständlich wird der endgültige Code in diesem Fall etwas komplexer ausfallen (Indikator dAMA.mq5), doch nun können wir die von mir am Anfang dieses Beitrags empfohlene Methodik nutzen, um Tests für beide Fälle durchzuführen und die entsprechenden Schlüsse zu ziehen. Erhöhen wir diesmal den Testzeitraum auf ein Jahr.
Nach dem Durchlaufen des Tests im Logbuch des Strategietesters erhalten wir die benötigte Zeit zum Testen des Expert Advisors dAMA_Test.
Die Durchlaufzeit des Tests liegt mit 43.937 ms innerhalb eines vernünftigen Rahmens. Das kann von der entsprechenden Durchlaufzeit des Tests mit dem Expert Advisor dAMA!!!!!!_Test leider nicht behauptet werden.
Die Durchlaufzeit ist mit 960.625 ms zwanzig Mal länger als im vorherigen Fall. Die Schlussfolgerung ist offensichtlich. Der Code sollte auf die sparsamste mögliche Art geschrieben werden, damit er keine unnötigen Berechnungen durchführt!
Der anhand der oben beschriebenen Prinzipien aufgebaute AMkA-Indikator zeigt nichts Neues, sodass ich in diesem Fall nur auf die Details des Kopierens der Daten eingehe.
//---- declaring local arrays double dAMA_Array[], StdDev_Array[]; //---- indexation of elements in arrays just like in time series ArraySetAsSeries(dAMA_Array, true); ArraySetAsSeries(StdDev_Array, true); //--- calculation of the number of copied data int to_copy; if(prev_calculated > rates_total || prev_calculated <= 0)// verification of the first start of indicator calculation to_copy = rates_total - begin; // calculated number of all bars else to_copy = rates_total - prev_calculated + 1; // calculated number of only new bars //--- copy the newly appeared data into the indicator buffer and local dynamic arrays if(CopyBuffer(dAMAHandle, 1, 0, to_copy, AMABuffer ) <= 0) return(0); if(CopyBuffer(dAMAHandle, 0, 0, to_copy, dAMA_Array ) <= 0) return(0); if(CopyBuffer(StdDevHandle, 0, 0, to_copy, StdDev_Array) <= 0) return(0);
Alles geschieht absolut analog, mit der Ausnahme, dass die Daten nun in einen einzelnen Indikatorpuffer und zwei logisch deklarierte dynamische Arrays für Zwischenberechnungen kopiert werden.
Die Umsetzung aller Indikatorberechnungen innerhalb des Indikators als eine der Optimierungsmöglichkeiten
Das alles ist natürlich sehr interessant, aber eine so komplexe Struktur aus aufeinanderfolgenden Aufrufen von benutzerdefinierten und technischen Indikatoren sieht ziemlich verdächtig aus. Und sie sollte auch irgendwie gemessen werden. Doch dafür wäre es nicht schlecht, den Code des AMkA-Indikators zu haben, der sich innerhalb des benutzerdefinierten Indikators befinden würde und die Aufrufe anderer Indikatoren nicht nutzen würde.
Für einen Programmierer, der den Prozess zum Schreiben von Indikatoren in MQL5 vollständig begriffen hat, ist das kein großes Problem. Erst wird der Code des benutzerdefinierten Indikators AMA.mq5 geschrieben und dann werden die benötigten Elemente des Codes für die Umsetzung des Indikators AMkA_.mq5 hinzugefügt. Wir erhalten einen zweiten, großen Zyklus der Indikatorberechnung, in dem das benutzerdefinierte Array mit dem Inkrement des Indikators AMA befüllt wird:
//---- the main cycle of calculating the AMkA indicator for(bar = first; bar < rates_total; bar++) { //---- load the increments of the AMA indicator into the array for intermediate calculations for(iii = 0; iii < ama_period; iii++) dAMA[iii] = AMABuffer[bar - iii - 0] - AMABuffer[bar - iii - 1];
Anschließend wird dieses Array für die Durchführung von Operationen, die der Berechnung des technischen Indikators StDev auf Basis der Daten des Indikators dAMA.mq5 entsprechen, verwendet.
//---- find the simple average of increments of AMA Sum = 0.0; for(iii = 0; iii < ama_period; iii++) Sum += dAMA[iii]; SMAdif = Sum / ama_period; //---- find the sum of the square differences of increments and the average Sum = 0.0; for(iii = 0; iii < ama_period; iii++) Sum += MathPow(dAMA[iii] - SMAdif, 2); //---- determine the final value of the meansquare deviation of StDev from the increment of AMA StDev = MathSqrt(Sum / ama_period);
Der Rest des Codes entspricht vollständig dem Code des Indikators AMkA.mq5 und ist für uns nicht besonders interessant. Nun können wir anfangen, die Indikatoren AMkA_mq5 und AMkA.mq5 mithilfe der Expert Advisors AMkA__Test.mq5 und AMkA_Test.mq5 zu testen.
Beim Testen des Indikators AMkA_.mq5 entstehen keine Probleme und die Testzeit liegt in einem akzeptablen Rahmen.
Aber der Indikator AMkA.mq5 war unglaublich langsam.
Sein Ergebnis ist sieben Mal schlechter als das seines "Bruders". Was kann man dazu noch sagen? Der Schlussfolgerung ist offensichtlich: Derart komplexe Konstruktionen aus mehreren aufeinanderfolgenden Aufrufen von Indikatoren, einer aus dem anderen, sind nicht sehr vernünftig und nur für Vorabtests geeignet!
Natürlich haben wir diese Ergebnisse mit einer Testversion des Client Terminals erhalten und es ist schwer zu sagen, wie das Ganze in der Zukunft aussehen wird. Doch in Anbetracht der anstehenden Meisterschaft der Handelsroboter kann man ganz klar sagen, dass das ein sehr wichtiges und aktuelles Thema ist. [Anm. d. Übers.: Der russische Originalbeitrag, auf dem diese Übersetzung basiert, wurde am 29. Juni 2010 veröffentlicht.]
Einige Besonderheiten des Aufrufs von Indikatoren aus Expert Advisors
Alle Inhalte des Programmiercodes der Indikatoren, die mit der Optimierung des Zugriffs auf Benutzerdaten und technische Indikatoren in Verbindung stehen, lassen sich auch im Programmcode von Expert Advisors für die Optimierung des Zugriffs auf Benutzerdaten und technische Indikatoren anwenden. Zusätzlich zu den bereits vorgestellten Situationen gibt es einen weiteren Faktor im Code des Expert Advisors, der das Testing und die Optimierung von Handelssystemen grundlegend beeinflussen kann.
Eine große Menge an Expert Advisors verarbeitet die Indikatordaten für gewöhnlich nur zum Zeitpunkt des Balkenübergangs. Aus diesem Grund muss die Funktion CopyBuffer() in diesen Expert Advisors nicht für jeden Tick aufgerufen werden.
In dieser Situation müssen die Expert Advisors die Daten aus den Indikatorpuffern nur während eines Balkenübergangs erhalten. Deshalb müssen sich die Aufrufe der Indikatoren im Block hinter den Klammern befinden, auf den der Zugriff nur einmal pro Balkenübergang erlaubt ist, wenn alle erforderlichen Daten aus dem Indikatorpuffer erfolgreich in die Arrays für Zwischenberechnungen kopiert wurden.
Am besten dient als solcher Filter eine benutzerdefinierte Funktion, die zu dem Zeitpunkt, zu dem eine Änderung des aktuellen Balkens stattfand, eine logische Einheit ausgibt. Die Datei IsNewBar.mqh enthält meine ziemlich universell anwendbare Version dieser Funktion:
bool IsNewBar ( int Number, // Number of call to the function IsNewBar in the program code of the expert Advisor string symbol, // The chart symbol on which the data calculation is done ENUM_TIMEFRAMES timeframe // Timeframe of the chart on which the data calculation is done )
Wird diese Funktion im Code des Expert Advisors genutzt, sieht das beispielsweise so aus:
//---- declaration of a static variable - array for storing the values of the AMA indicator static double AMA_Array[3]; //---- calling up the AMA indicator for copying its values into the AMA_Array array if (IsNewBar(0, Symbol(), 0)) { CopyBuffer(AMA_Handle, 0, 1, 3, AMA_Array); }
Es ist in diesem Fall allerdings viel vernünftiger, anders zu handeln. Wenn Sie CopyBuffer() aufrufen, werden die Daten möglicherweise nicht in das Array AMA_Array[] kopiert. In diesem Fall müssen Sie diese Funktion für jeden Tick aufrufen, bis es eine erfolgreiche Option zum Kopieren der Daten gibt, die durch eine gewisse Verkomplizierung des Filters umgesetzt wird:
//---- declaration of a static variable - array for storing values of the AMA indicator static double AMA_Array[3]; //---- declaration of the static variable for storing the results of copying the data from the AMA indicator static bool Recount; //---- calling up the AMA indicator for copying its values into the AMA_Array array if (IsNewBar(0, Symbol(), 0) || Recount) { if (CopyBuffer(AMA_Handle, 0, 1, 3, AMA_Array) < 0) { Recount = true; // attempt of data copying was unsuccessful return; // exit the function OnTick() } //---- All operations of copying from the indicator buffers are successfully completed // there is no need for returning to this block until the next bar change Recount = false; }
Da nun die Details des rationalen Aufrufs der Kopierfunktion der Indikatorwerte im Code des Expert Advisors geklärt sind, können Sie die Vorteile der Anwendung der Funktion IsNewBar() in Expert Advisors testen.
Wir verfügen also über zwei mögliche Expert Advisors, die im Strategietester getestet werden können. Der erste ist AMA_Test.ex5. Er kopiert bei jedem Tick die Daten aus dem Indikatorpuffer.
Der zweite, IsNewBar_AMA_Test.mq5, kopiert die Daten nur bei einem Balkenübergang.
Ja! Die Testergebnisse sind etwas enttäuschend. Es hat sich herausgestellt, dass der Aufruf der Funktion IsNewBar() bei jedem Tick viel zeitintensiver ist als das Kopieren der Daten in drei Zellen des benutzerdefinierten Arrays!
Hier möchte ich Ihre Aufmerksamkeit auf einen weiteren wichtigen, aber scheinbar unauffälligen Teil des Indikators lenken. Wenn wir das Handle des Indikators in der Funktion OnInit() erhalten, werden seine Berechnungen für noch nicht berechnete und aktuelle Balken unabhängig davon, ob wir die Daten aus diesem Indikator innerhalb der Funktion OnTick() kopieren, weiterhin bei jedem Tick durchgeführt.
Wenn also unser Expert Advisor die gezählten Indikatorwerte aus dem aktuell offenen Balken nicht benötigt, ist es in Anbetracht der Zeitersparnis besser, die Berechnung dieser Werte zu deaktivieren. Das ist ziemlich einfach: Reduzieren Sie die rechte Grenze des Hauptzyklus der Neuzählung der Balken im Indikator um eins. Vor den Änderungen sah dieser Zyklus im Indikator AMA.mq5 so aus:
//---- main cycle of indicator calculation for(bar = first; bar < rates_total; bar++)
Nach der Änderung wird er so aussehen:
//---- main cycle of indicator calculation for(bar = first; bar < rates_total - 1; bar++)
Indikator AMA_Ex.mq5. Nun können Sie diesen Indikator (Expert Advisor AMA_Ex_Test.mq5) testen.
Natürlich ist dieses Ergebnis 21 % besser als der Test des Indikators AMA, was gar nicht schlecht ist, aber wenn wir es uns nochmal überlegen, könnte das Ergebnis noch viel besser sein.
Fazit
Alles in allem ist die Effizienz des Codes eines Programms eine ziemlich objektive Angelegenheit. Effizienz kann gemessen, logisch analysiert und in bestimmten Situationen deutlich erhöht werden. Die Methoden dazu sind nicht sehr kompliziert. Man braucht nur etwas Geduld und muss etwas mehr tun als nur, was sich direkt auf die Wirtschaftlichkeit des automatisierten Handelssystems auswirkt.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/109
![Die Verwendung von ORDER_MAGIC für den Handel mit unterschiedlichen Expert Advisors mit einem Instrument](https://c.mql5.com/2/0/order_magic_MQL5__1.png)
![Die optimale Berechnungsmethode für das Gesamtvolumen an Positions nach der festgelegten Magischen Zahl](https://c.mql5.com/2/0/calculate_volume_MQL5__1.png)
![Bibliothek für die Erstellung von Diagrammen über die Google Chart API](https://c.mql5.com/2/0/Google_Chart_API_MQL5__1.png)
![Verwendung der TesterWithdrawal() Funktion zur Nachahmung der Gewinnentnahme](https://c.mql5.com/2/0/TesterWithdrawal_MQL5.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.