Libraries: CDouble & CDoubleVector - page 2

 
Alain Verleyen:
Good catch. However from a trading point of view, you need either 1.70060 or 1.70061, they are both correct. So maybe you will want to chose the best one according to your trading operation, rather than relying on a mathematical rounding scheme.

In this scenario, if your step-size == 0.00001 then you should only get 1.70061 as a result, and a value of 1.70060 is close but incorrect. 

 
nicholishen:

In this scenario, if your step-size == 0.00001 then you should only get 1.70061 as a result, and a value of 1.70060 is close but incorrect. 

Sorry but I don't understand your reasoning. What is "step-size" ?

 
Alain Verleyen:

Sorry but I don't understand your reasoning. What is "step-size" ?

step-size is the value of the step that you are rounding to. eg. lot_step = 0.01 (2 digits), or tick_size = 0.00005...


In this example-case, we are working with a 5 digit symbol with a tick-size == 0.00001

 
nicholishen:

step-size is the value of the step that you are rounding to. eg. lot_step = 0.01 (2 digits), or tick_size = 0.00005...


In this example-case, we are working with a 5 digit symbol with a tick-size == 0.00001

Ok so step-size is tick-size. That still doesn't say me why from a trading point of view a calculated price like 1.700605 could not be "rounded" (whatever the mathematical/coding way) to 1.70060 ?
 
Alain Verleyen: Ok so step-size is tick-size. That still doesn't say me why from a trading point of view a calculated price like 1.700605 could not be "rounded" (whatever the mathematical/coding way) to 1.70060 ?

Please understand that a "trading point of view" is not relevant here. This is purely a mathematical issue for which @amrali has provided a solution.

There is no need to discuss the "trading point of view" here as it is off-topic for this thread and will ultimately escalate into a another heated debate ending in someone being banned again. So, please try to avoid that!

 
Fernando Carreiro:

Please understand that a "trading point of view" is not relevant here. This is purely a mathematical issue for which @amrali has provided a solution.

There is no need to discuss the "trading point of view" here as it is off-topic for this thread and will ultimately escalate into a another heated debate ending in someone being banned again. So, please try to avoid that!

I am just trying to understand what @nicholishen said. There is no reason for an heated debate or whatever. This is a forum about trading, isn't ?

Please explain me why from a calculated price of 1.700605, a real price of 1.70060 is "incorrect" ? If I am missing something I would like to understand it.

 
Alain Verleyen:

I am just trying to understand what @nicholishen said. There is no reason for an heated debate or whatever. This is a forum about trading, isn't ?

Please explain me why from a calculated price of 1.700605, a real price of 1.70060 is "incorrect" ? If I am missing something I would like to understand it.

Like Fernando said, "this is purely a mathematical issue", and in the mathematical sense the expected result is for the 5 to be rounded up to the nearest digit instead of down. The MathRound function is producing inconsistent results and if you aim to avoid inconsistency then I'd advise using ND instead, as wisely suggested by @amrali. To get us all back on topic, the CDouble library no longer uses the MathRound function in favor of more consistent results. 

 
Alain Verleyen:

I am just trying to understand what @nicholishen said. There is no reason for an heated debate or whatever. This is a forum about trading, isn't ?

Please explain me why from a calculated price of 1.700605, a real price of 1.70060 is "incorrect" ? If I am missing something I would like to understand it.

Hi Alain Verleyen, you are correct regarding your point. For a single calculation, it may not make much difference. However, as you do more calculations which involve rounding at each step, the final error will be magnified. Consider a martingale strategy, where each calculated lotsize is derived from the previous one (which was sent to the trading server as a rounded value). The issue we consider here is called "Propagation of errors". So, I think it would be more safe to use NormalizeDouble() in order to keep the intermediate errors as low as possible.

Second, there also exists a more technical point here. Using the function MathRound(number * power) / power  with edge numbers, you will get either the upper rounded value sometimes, or the lower rounded value, the other times depending on the input. This inconsistency in rounding may not matter from a trading point of view, as you mentioned in your comment, as long as you get a final rounded value (either up or down), so the trading server will not complain. However, as stated by Mark Watkinsuch inconsistency in rounding may introduce hard to detect bugs in the client code that uses the "CDouble" library.

By the way, using another variant of the above function like  MathRound(number / point) * point  with edge numbers, you get a rounded number (either up or down), or more interestingly you do not get an exact rounded number! (the result differs from the expected value by 1 Epsilon, but it is OK for trading functions)

The following script helps to demonstrate these issues, more clearly:

#property strict

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

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
// inconsistent results of MathRound() with edge numbers

void OnStart()
{
    // round prices half-way to the nearest 5 decimal digits

    PRINT(MathRound(1.248825 * 100000) / 100000 == 1.24883);    // true, that price gets rounded up (as expected)
    PRINT(MathRound(1.248835 * 100000) / 100000 == 1.24883);    // true, that price gets rounded down this time
    PRINT(MathRound(1.248845 * 100000) / 100000 == 1.24885);    // true, that price is rounded up again

    // the second variant of MathRound()

    PRINT(MathRound(1.248825 / 0.00001) * 0.00001 == 1.24883);   // true, that price is rounded up (as expected)
    PRINT(MathRound(1.248835 / 0.00001) * 0.00001 == 1.24883);   // true, that price is rounded down this time
    PRINT(MathRound(1.248845 / 0.00001) * 0.00001 == 1.24885);   // false, that price is not rounded up exactly!!
    PRINT(MathRound(1.248845 / 0.00001) * 0.00001 == 1.24884);   // false, the same price is not rounded down exactly!!
    PRINT(MathRound(1.248845 / 0.00001) * 0.00001 == 1.2488400000000001);   // true, but it is OK for trading functions. 
}

Therefore, it would be better to replace MathRound(number) with NormalizeDouble(number, 0) to avoid inconsistent results when arithmetic (mid-point) rounding is indicated.

The other good alternative for mid-point rounding: use MathRound() + apply half-epsilon correction.

(This was implemented in the MathRoundCorrect() function as posted before).

double MathRoundCorrect(double num, int precision) {
        double c = 0.5 * DBL_EPSILON * num;
//      double p = MathPow(10, precision);  //slow
        double p = 1; while (precision--> 0) p *= 10;
        if (num < 0)
                p *= -1;
        return MathRound((num + c) * p) / p;
}

According to this wikipedia article, there many other rounding modes that serve different puposes. You are free to choose the one that fits your requirements. Best regards.

https://en.wikipedia.org/wiki/Rounding

Rounding - Wikipedia
Rounding - Wikipedia
  • en.wikipedia.org
Graphs of the result, y, of rounding x using different methods. For clarity, the graphs are shown displaced from integer y values. In the SVG file, hover over a method to highlight it and, in SMIL-enabled browsers, click to select or deselect it. Rounding a numerical value means replacing it by another value that is approximately equal but has...
Reason: