Arbeiten mit Doubles in MQL4

MetaQuotes | 4 Mai, 2016

Einführung

Die MQL Programmierung eröffnet neue Möglichkeiten für das automatisierte Trading, viele Menschen auf der ganzen Welt schätzen es bereits.

Wenn wir einen Expert Advisor für das Trading schreiben, müssen wir sicher sein, dass er korrekt arbeitet.

Viele Neulinge haben oft ein paar Fragen, wenn die Ergebnisse einiger mathematischer Berechnungen von den erwarteten abweichen. Das Programm ist kompiliert und kann arbeiten, aber nicht so wie es sollte. Sie prüfen den Code wieder und wieder, finden neue "Fehler" in der Sprache, in der Umsetzung, in Funktionen, usw.

In den meisten Fällen zeigt die sorgfältige Analyse, dass die Sprache und Compiler richtig funktionieren, aber der Code hat einen kleinen Fehler, und es kann eine lange Zeit dauern, ihn zu finden und zu korrigieren.

In diesem Artikel werden wir typische Programmierfehler betrachten, die bei der Arbeit mit double Nummern in MQL4 Programmen auftreten.


1. Prüfen der Nummerischen Werte

Um die Berechnungsergebnisse zu überprüfen und zu debuggen, können Sie die Funktion DoubleToStrMorePrecision(double number, int precision) aus der Standard-Bibliothek stdlib.mq4 verwenden, die es ermöglicht double Zahlen auf die angegebene Genauigkeit zu prüfen.

Es wird Ihnen ermöglichen Zeit bei der Suche nach möglichen Fehlern zu sparen.

Ein Beispiel:

#include <stdlib.mqh>
int start()
  {
   double a=2.0/3;
   Alert("Standard output:",a,", 8 digits precision:",DoubleToStr(a,8),", 15 digits precision:", DoubleToStrMorePrecision(a,15));
   return(0);
  }  

Ergebnis:

Standard-Ausgabe:0.6667, 8 Stellen Genauigkeit:0.66666667, 15 Stellen Genauigkeit:0.666666666666667

In manchen Fälle, um die nummerischen Werte der double Zahlen (zum Beispiel, in Print, Alert, Comment) ist es besser die Funktionen DoubleToStr und DoubleToStrMorePrecision (von stdlib.mq4) zu verwenden um genauere Werte anzuzeigen, statt der Standard 4 Stellen Ausgabegenauigkeit.

Zum Beispiel:

#include <stdlib.mqh>
int start()
  {
   double a=2.0/100000;
   Alert("Standard output=",a,", More precise output=",DoubleToStrMorePrecision(a,15));
   return(0);
  }

Rückgaben: "Standard output=0, Präziser output=0.000020000000000".


2. Genauigkeit der Dezimalstellen Präzision

Wegen dem doppelte Genauigkeit Gleitkomma Format gibt es eine begrenzte Genauigkeit ihrer Speicherung.

Wenn wir beispielsweise annehmen, dass wir eine unbegrenzte Präzision haben, wie in der Theorie, sind für alle double Zahlen A und B die folgenden Ausdrücke immer gültig:

(A/B)*(B)=A,

A-(A/B)*B=0,

(A/B)*(B/A)=1 etc.

Die Genauigkeit der Dezimalziffern-Speicherung in einem Computer ist abhängig von der Fraktionsgröße und begrenzt auf 52 Bits. Um diese Tatsache zu veranschaulichen, betrachten wir das folgende Beispiel.

In dem ersten Zyklus (i) berechnen wir die Fakultät von 23 (Produkt der ganzen Zahlen von 2 bis 23) und das Ergebnis ist: 23!=25852016738884976640000. Das Ergebnis wird in der a Variable des double Typs.

in dem nächsten Zyklus (j), teilen wir den resultierenden Wert a durch alle Ganzahlen von 23 bis 2. Es scheint, wir können schließlich erwarten, dass a=1.

#include <stdlib.mqh>
int start()
  {
   int maxfact=23;
   double a=1;
   for (int i=2; i<=maxfact; i++) { a=a*i; }
   for (int j=maxfact; j>=2; j--) { a=a/j; }
   Alert(" a=",DoubleToStrMorePrecision(a,16));
   return(0);
  }

Stattdessen haben wir:

a=1.0000000000000002

Wie wir sehen, haben wir eine Ungenauigkeit in der 16. Ziffer.

Wenn wir die Berechnung auf 35! erhöhen, erhalten wie a=0.9999999999999998.

Die MQL Sprache hat eine Funktion NormalizeDouble, die es ermöglicht eine double Zahl auf die angegebene Genauigkeit zu runden.

Die Konstanten des double Typs werden im Speicher auf ähnliche Weise gespeichert wie double Variablen, daher ist es erforderlich die Grenze von signifikanten Ziffern in ihrer Definition zu beachten.

Aber verwechseln sie nicht die Genauigkeit der Dezimalstellen Präzision mit der Berechnungsgenauigkeit für die double Zahlen.

Der Bereich der der möglichen Werte für die double Zahlen ist viel breiter: von -1.7*e-308 bis 1.7*e308.

Lassen sie uns versuchen den kleinsten Exponenten der double Zahl zu schätzen.
int start()
  {
  double R=1;
  int minpwr=0;
  while (R>0) {R=R/10; minpwr--;}
  Alert(minpwr);
  return(0);
  }

Das Programm wird -324 ausgeben, aber wir müssen die Dezimalstellen in Fraktion (15) berücksichtigen und wir werden einen ungefähren Wert für den Exponenten der kleinsten double Zahl erhalten.


3. Funktion NormalizeDouble

Die Funktion NormalizeDouble (double Wert, int Ziffern) rundet den Fließkommawert auf die angegebene Präzision, gibt den normalisierten Wert des double Typs zurück.

Zum Beispiel:

int start()
  {
   double a=3.141592653589;
   Alert("a=",DoubleToStr(NormalizeDouble(a,5),8));
   return(0);
  }

das Ergebnis ist:

a=3.14159000

Beachten Sie, dass es in Handelsoperationen unmöglich ist die nicht normalisierten Kurse zu verwenden, deren Genauigkeit zumindest um eine Stelle die durch einen Handelsserver geforderte überschreitet.
Der StopLoss, TakeProfit und Kurs Wert für die Pending Ordern sollte mit der Genauigkeit normalisiert werden, deren Wert gespeichert ist in vorgegebenen variablen Ziffern.


4. Prüfen auf Gleichheit von zwei double Zahlen

Es wird empfohlen zwei double Zahlen mit der Funktion CompareDoubles(double number1,double number2) der stdlib.mq4 Bibliothek zu vergleichen, was wie folgt aussieht:

//+------------------------------------------------------------------+
//| correct comparison of 2 doubles                                  |
//+------------------------------------------------------------------+
bool CompareDoubles(double number1,double number2)
  {
   if(NormalizeDouble(number1-number2,8)==0) return(true);
   else return(false);
  }

Diese Funktion vergleicht number1 und number2 des double Typs mit einer Genauigkeit von bis zu 8 Dezimalstellen.

Das Beispiel:

#include <stdlib.mqh>
int start()
  {double a=0.123456781;
   double b=0.123456782; 
   if (CompareDoubles(a,b)) {Alert("They are equal");}
   else {Alert("They are different");}
  }

wird ausgeben:

Sie sind gleich

weil sie sich nur in der 9. Stelle unterscheiden.

Wenn erforderlich, können Sie Ihre eigene Vergleichsfunktion (mit der gewünschten Genauigkeit) in ähnlicher Weise schreiben.


5. Ganze Zahlen Dividieren

Es ist erforderlich daran zu denken, dass wenn wir zwei ganze Zahlen (Integer) dividieren, werden wir eine ganze Zahl als Ergebnis erhalten.

Deshalb wird der Code:

int start()
  {
   Alert(70/100);
   return(0);
  }

0 ausgeben, weil 70 und 100 ganze Zahlen sind.

So wie in C/C++, in MQL ist das Ergebnis einer Division von zwei ganzen Zahlen wird eine ganze Zahl sein, in diesem Fall ist es 0.

Ist jedoch der Zähler oder der Nenner double (d.h. hat einen fraktionellen Teil), wird das Ergebnis ein double sein. Daher Alert (70/100.0), wird den richtigen Wert ausgeben 0.7.

Zum Beispiel:
int start()
  { double a=1/3;
    double b=1.0/3;
   Alert("a=",a,", b=",b);
   return(0);
  }

wird ausgeben "a=0, b=0.3333"

6. Typisieren für integer und double Zahlen

Betrachten wir den Code:
double xbaseBid=1.2972;
double xBid=1.2973;
double xPoint=0.0001;
int i = 100 + (xBid - xbaseBid)/xPoint;
Alert(i);

Wir werden 100 erhalten, aber es scheint es sollte 101 sein, wegen der offensichtlichen Gleichheit: 0.0001/0.0001=1

Das gleiche Beispiel in C/C++:
double baseBid=1.2972,Bid=1.2973,Point=0.0001;
int i = 100 + (Bid - baseBid)/Point;
printf("%d\n",i);

ergibt ebenso 100.

um den Grund für diese Tatsache zu bestimmen, betrachten wir den Code:

double a=0.99999999999999;
int i = 100 + a;
Alert(i);

der ergibt i=100.

Wenn wir jedoch eine gewisse Genauigkeitsverbesserung für a durchführen:

double a=0.999999999999999;
int i = 100 + a;
Alert(i);

dann werden wir 101 erhalten.

Der Grund für diese Tatsache ist die Verwendung von integer und double Zahlen, kompliziert durch geringe Genauigkeit Werte.

Daher, bei Verwendung der Operationen dieser Art ist es empfehlenswert die Rundung der ähnlichen Ausdrücke mit der Funktion MathRound(double Wert)auszuführen
die den gerundeten double Wert zu dem nächsten integer zurückgibt:

double baseBid=1.2972;
double xBid=1.2973;
double xPoint=0.0001;
int i = 100 + MathRound((xBid - baseBid)/xPoint);
Alert(i);

In einem solchen Fall erhalten wir den richtigen Wert 101.

Es ist ein häufiger Fehler bei der Verwendung der Funktion OrderModify, insbesondere bei der Trailing Stop Programmierung. Die Funktion OrderModify gibt einen Fehler N°1 zurück: ERR_NO_RESULT in dem Fall, wenn die neuen Werte für die Ordern die gleichen sind wie bereits definiert. Also ist es erforderlich eine sorgfältige Prüfung für diese Gleichheit durchzuführen (mit NormalizeDouble) und eine sorgfältige Punktgröße Berechnung.

Denken Sie daran, dass das Client-Terminal nur ermöglicht die Order-Parameter zu ändern, wenn die neuen Werte von den bereits definierten um mindestens 1 Punkt abweichen.


7. Eigenschaften der Funktion MathMod


In MQL entspricht die MathMod (double v1, double v2) Funktion vollständig der Funktion fmod (double v1, double v2) von MSVC6, weil für den direkten Aufruf für die fmod Funktion die C Runtime Bibliothek verwendet wurde..

In einigen Fällen gibt die Funktion fmod von MSVC6 (und auch MathMod) falsche Ergebnisse.

Wenn Sie die Funktion MathMod in Ihrem Programm verwenden, bitte ersetzen Sie diese mit der folgenden Funktion:
double MathModCorrect(double a, double b)
{ int tmpres=a/b;
return(a-tmpres*b);
}

Beachten Sie, dass diese Feststellung nur für MQL4 gilt, MQL5 berechnet diese Funktion durch seine mathematische Definition.


Fazit

Beachten Sie, dass die obige Liste nicht vollständig ist, wenn Sie etwas neues gefunden haben, das hier nicht beschrieben wurde, schauen Sie in die Kommentare.

Wenn Sie keine Lösung gefunden haben, beschreiben Sie es in den Kommentaren, fügen Sie Ihren Code ein und die MQL-Community Fachleute werden Ihnen helfen.