NormalizePrice() any suggestions on how to do this better - page 3

 
Dominik Christian Egert #:
I should explain where the issue is coming from.

I take the ATR and half it. I add these two values to a given level and subtract them from the same. Now I need to compare these values to the current price.

So if I add them, then in some situations the value is lower than the price even if it (when rounded correctly) weren't. So it's off by a fraction. But this leads to wrong assumptions.

Also if (I know it's not the case for this Examen, but there are situations in which this applies) I divide 0.00009, I get as result 0.000044999999 so this rounds to 0.00004, which is wrong for my use case. It would need to be 0.00005.

I didn't expect this to be so precisely working, but if I ignore this error, I get bad results. If I manage this, results are correct.

Well, that's the source.

So I was thinking how to solve this and therefore I wrote my own function to "Normalize Price".

So, yes, I need to round the value. Especially because ATR values are way off from being "compatible" with price values.

That's my issue at the moment.
The issue is that you still trying to deal with doubles like integers when comparing them, they are not precise, you have to define
an acceptable margin of error, in the case of 5 digit EURUSD, that would be 0.00001, so a simple function to solve the issue is:

bool is_equal(double a, double b) {
  return MathAbs(a - b) < .00001;
}
You could of course add a precision parameter and improve this function.
 
Dominik Christian Egert #:

So I have investigated on the rounding function of NormalizePrice

Here is my finding

std-lib:

will give as result: 1.3559999999999999

while my function will return: 1.3560000000000001

Now the question is, which is better?

Better use this function to display doubles instead of (string)dbl or Print(dbl);

It is equivalent to repr of python.

//+------------------------------------------------------------------+
//| Converting numeric value into the shortest string representation |
//| that round-trips into the same numeric value. The result may hold|
//| up to the first 17 significant digits, discarding trailing zeros.|
//| The round-trip ("%.17g") format specifier ensures that a numeric |
//| value converted to a string is always parsed back into the same  |
//| numeric value, StringToDouble(Repr(f)) == f.                     |
//+------------------------------------------------------------------+
// toString()
string Repr(double value)
  {
   string repr = "";
//--- First format with 15 spaces of precision. If round-trip fails,
//--- format with 17 spaces of precision.
   if(value == StringToDouble(repr = StringFormat("%.15g", value)))
      return repr;

   return StringFormat("%.17g", value);
  }
 
Thank you. I know about that issue. I always use printf(), and I always use formatting strings...

It's not about displaying. It's about the precision compromise and applying the right granularity to calculations and comparison.

Thank you for your support.

When comparing doubles, I only use greater/less than to be sure to get consistent results. But still there stays a margin or error due to the calculations and their results and the given granularity.

Sometimes this is proned to an offset as described above and therefore gives false results.
 
Dominik Christian Egert #:
Thank you. I know about that issue. I always use printf(), and I always use formatting strings...

It's not about displaying. It's about the precision compromise and applying the right granularity to calculations and comparison.

Thank you for your support.

To check your functions for correct rounding (up, down or truncating) you can use this:

//+------------------------------------------------------------------+
//| Checks if a number has a specified number of fractional digits.  |
//+------------------------------------------------------------------+
bool IsRound(double num, int digits)
  {
// return num == StringToDouble(DoubleToString(num, digits));
   double p = MathPow(10, digits);
   return MathRound(num * p) / p == num;
  }

Welcome!

 
I will take into account all given hints and advices and try to solve the issue for me.

Thank you everyone for their contribution.
 
Dominik Christian Egert #:
I will take into account all given hints and advices and try to solve the issue for me.

Thank you everyone for their contribution.

One final hint that might help you is that, I always find the most reliable way to debug any rounding function is to use some test cases that involve exact comparisons with the hard-coded values of the expected answers.

DO NOT rely on the visual output of Print or any string function, as this involves converting the double answer to string, which is another source of error in itself.

I include an example of how to correctly test your function:

#define PRINT(A) Print(#A + " = ", (A))

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- testing simple cases
   PRINT(5.1     == NormalizePrice(5.12, 1));
   PRINT(-5.1    == NormalizePrice(-5.12, 1));

//--- testing edge cases for round
   PRINT(1.01    == NormalizePrice(1.005, 2));
   PRINT(-1.01   == NormalizePrice(-1.005, 2));
   PRINT(39.43   == NormalizePrice(39.425, 2));
   PRINT(-39.43  == NormalizePrice(-39.425, 2));

//--- testing round to digits
   PRINT(1.14    == NormalizePrice(1.14, 2));
   PRINT(1.93    == NormalizePrice(1.93, 2));
   PRINT(1.378   == NormalizePrice(1.378, 3));
   PRINT(1.757   == NormalizePrice(1.757, 3));
   PRINT(1.26008 == NormalizePrice(1.26008, 5));
   PRINT(1.54289 == NormalizePrice(1.54289, 5));
   PRINT(1.57373 == NormalizePrice(1.57373, 5));
  }


if anytime you need to use Print(dbl), use Print(Repr(dbl)) instead;

   Print(Repr( NormalizePrice(1.54289, 5) ));


because in MQL, this equality does not hold for a lot of numbers. 

   StringToDouble((string)dbl) == dbl;


So, what you see is not aways what you get. For example, consider this simple calculation

   double f = 0.1 + 0.2;

   PRINT( (string)f );                          // "0.3"
   PRINT( Repr(f) );                            // "0.30000000000000004"

//--- test round-tripping
   PRINT( f == StringToDouble((string)f) );     // false
   PRINT( f == StringToDouble(Repr(f)) );       // true
 
amrali #:

One final hint that might help you is that, I always find the most reliable way to debug any rounding function is to use some test cases that involve exact comparisons with the hard-coded values of the expected answers.

DO NOT rely on the visual output of Print or any string function, as this involves converting the double answer to string, which is another source of error in itself.

I include an example of how to correctly test your function:


if anytime you need to use Print(dbl), use Print(Repr(dbl)) instead;


because in MQL, this equality does not hold for a lot of numbers


For example, consider this simple calculation

What is this "NormalizePrice" function ? There is no need for a second parameter "digits" or "decimals" to normalize a price in the mql sense.
 
Alain Verleyen #:
What is this "NormalizePrice" function ? There is no need for a second parameter "digits" or "decimals" to normalize a price in the mql sense.

Hi Alain, NormalizePrice is the proposed function of the OP.

 

I posted this answer on StackOverflow some time ago, for more in-depth handling of rounding with 64-bit doubles.

https://stackoverflow.com/a/48764436/4208440

 
amrali #:

Hi Alain, NormalizePrice is the proposed function of the OP.

Ah ok. Sooner or later this function will result in buggy values.

Reason: