MQL4 MathCeil() intermittantly returns wrong result

 

Coming from thread https://forum.mql4.com/21235 a Discussion on double arithmetic conditional relationships and HW/SW dependencies...


I have run into a problem with the MathCeil() function to determine the correct percent of lots to return to caller for a percent 100. For some reason ( double arithmetic) the MathCeil() it will intermittantly increase an integer rather than returning the integer. When the lots are converted back to the proper type, an error 131 is thrown because we now have one more lot than can be closed.

//+------------------------------------------------------------------+
//|                                                  PercentLots.mq4 |
//|                  Copyright © 2009, Vampire Trading Network, Inc. |
//|                                 http://VampireTradingNetwork.com |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2009, Vampire Trading Network, Inc."
#property link      "http://VampireTradingNetwork.com"

//+------------------------------------------------------------------+
//| PercentLots (double percent)
//|   Compute lots based on normalized percent
//--------------------------------------------------------------------
//| percent    Normalized percent 1 == 100 .5 = 50
//| Lots       pre calculation lots on OrderClose
//| _lots      percent lots to Close
//| lotStep    MathInfo(Symbol, MODE_LOTSTEP)
//| 
//| lots       Global lots to remain on trade
//| 
//| return     percent _lots to close
//+------------------------------------------------------------------+
double PercentLots(double percent) {
   // normalize the lots
   double   Lots  = lots,
            _lots = lots / lotStep;
   
   // calculate the percent lots and remainder
   _lots = MathCeil(_lots * percent) * lotStep;

   // update global lots left to trade
   lots  = lots - _lots;
   Log (DEBUG_ORDERS, StringConcatenate("PercentLots(): percent: ", percent, " Lots: ", Lots, " _lots: ", _lots, " lots: ", lots));
   // return the ceiling lots
   return(_lots);
}

This results in three profitable orders being errored out of 16 over a 4 month test on 1 hour timeframe.

21:56:37 2008.09.15 12:32  VTN Expert Advisor I EURUSD,H1: DEBUG_ORDERS: PercentLots(): percent: 1 Lots: 0.56 _lots: 0.57 lots: -0.01
21:56:37 2008.09.15 12:32  VTN Expert Advisor I EURUSD,H1: OrderClose error 131

21:56:39 2008.09.15 21:35  VTN Expert Advisor I EURUSD,H1: DEBUG_ORDERS: PercentLots(): percent: 1 Lots: 1.12 _lots: 1.13 lots: -0.01
21:56:39 2008.09.15 21:35  VTN Expert Advisor I EURUSD,H1: OrderClose error 131

21:57:51 2008.10.08 08:01  VTN Expert Advisor I EURUSD,H1: DEBUG_ORDERS: PercentLots(): percent: 1 Lots: 2.49 _lots: 2.5 lots: -0.01
21:57:51 2008.10.08 08:01  VTN Expert Advisor I EURUSD,H1: OrderClose error 131

Additionally I found that the -zero was being returned

21:58:42 2008.10.20 13:23  VTN Expert Advisor I EURUSD,H1: DEBUG_ORDERS: PercentLots(): percent: 1 Lots: 1.4 _lots: 1.4 lots: -0
21:58:42 2008.10.20 13:23  VTN Expert Advisor I EURUSD,H1: close #19 sell 1.40 EURUSD at 1.3430 sl: 1.3510 tp: 1.3380 at price 1.3403

21:59:17 2008.10.28 20:54  VTN Expert Advisor I EURUSD,H1: DEBUG_ORDERS: PercentLots(): percent: 1 Lots: 0.94 _lots: 0.94 lots: -0
21:59:17 2008.10.28 20:54  VTN Expert Advisor I EURUSD,H1: close #21 buy 0.94 EURUSD at 1.2575 sl: 1.2443 tp: 1.2658 at price 1.2618

22:00:18 2008.11.13 11:08  VTN Expert Advisor I EURUSD,H1: DEBUG_ORDERS: PercentLots(): percent: 1 Lots: 1.61 _lots: 1.61 lots: -0
22:00:18 2008.11.13 11:08  VTN Expert Advisor I EURUSD,H1: close #25 buy 1.61 EURUSD at 1.2504 sl: 1.2430 tp: 1.2544 at price 1.2523

I guess the easy fix here is

   if (percent == 1) return (lots);

Looking forward to more stimulating Double arithmetic logic.

Thanks

John

 
Replace
_lots = lots / lotStep;
to
_lots = NormalizaDouble(lots,2) / lotStep;
 
Roger:
Replace
to

Fair enough,

But as a software engineer I have to ask several questions here.


  1. How much valuable cycles am I loosing due to repetitive and redundant NormalizeDouble (ND) usage. I am beginning to feel that we are excessively NDing everything all the time.
  2. I have to question a high level language that requires me to re ND a value which was already ND'd prior to putting the value into a variable or array. Do we have to call the ND() function each and every usage of a double.
  3. In your example, why wouldn't it be more proper to
   _lots = NormalizaDouble(lots / lotStep,2);

Why does MQLX not accept and maintain the integrity of a simple double whose significance is well within the percision of a 16 bit machine let alone a 64 bit. I have been programming for a long time, granted not financials which leaves me out of a large amount of arithmetic formula, but I have never had to take such extremes to protect my data integrity.


I think that MetaQuotes should really present a clear article on the usage of double arithmetic and relationship usage so as to allow newbie and seasoned programmers alike the opportunity to design their EA/scripts properly. It has been a bear trying to change all my double usage to what I feel is clearly a work around, but as I stated in other posts, if this INDEED is an effective way of keeping processing cycles up, I will submit to the idiosyncrasy.


OH,

can a EA expert please confirm that we a Double data that has been stored in a var or array needs to be re normalized?


Thanks


John

 

Hello Friends,


MathCeil () is intended for number value heightening.
In aforeposted code MathCeil () do what it must do: attempts to heighten number value.
MathRound () should have been used in aforeposted computations.


Best regards,
Ais

 
johnmcglaughlin:

How much valuable cycles am I loosing due to repetitive and redundant NormalizeDouble (ND) usage

My very average hardware can do about 5 million calls to NormalizeDouble per second. Personally, this is miles above any sort of threshold where I've had to worry about it.


<strong><span style=" color:="" rgb(66,="" 99,="" 156);="" font-family:="" tahoma,="" verdana,="" arial,="" helvetica,="" sans-serif;="" font-size:="" 1em;="">johnmcglaughlin wrote <a href="/ 21341#157395"="" title="View message">johnmcglaughlin wrote >>

I have to question a high level language that requires me to re ND a value which was already ND'd prior to putting the value into a variable or array

In your specific example of 100% of 0.56 lots, the problem is that the floating point unit calculates lots / lotStep (0.56 / 0.01) as 56.000000000000007. MathCeil(_lots * 1) therefore rounds it up to 57, eventually leading to a return value of 0.57 lots.


<strong><span style=" color:="" rgb(66,="" 99,="" 156);="" font-family:="" tahoma,="" verdana,="" arial,="" helvetica,="" sans-serif;="" font-size:="" 1em;="">johnmcglaughlin wrote <a href="/ 21341#157395"="" title="View message">johnmcglaughlin wrote >>

Why does MQLX not accept and maintain the integrity of a simple double [...] can a EA expert please confirm that we a Double data that has been stored in a var or array needs to be re normalized? 

Ais is probably your man for a definitive answer, but a need for re-normalization sounds very unlikely. It's not the case - or, certainly not here - that a value which you're storing in a double is somehow mysteriously changing of its own volition.


Personally, I'd instead question whether the best compromise between speed and sense isn't for functions such as MathCeil() etc to do an automatic internal NormalizeDouble on their parameters. It's a bit daft for them to be working to 15 decimal places of precision.


<strong><span style=" color:="" rgb(66,="" 99,="" 156);"="">Ais wrote <a href="/ 21341#157395"="" title="View message">Ais wrote>>

MathRound () should have been used in aforeposted computations. 

Doesn't that give rise to a potential problem (e.g. lots = 0.1 and percent <= 0.04) where the function can return zero? Surely, in context, either MathCeil is more appropriate, albeit with more cautious usage, or there should also be a check that the value returned is at least MODE_MINLOT?


 

Hi again,


MathRound () should be used especially and exactly for zero returning, because, for example:
1. if "lots < MinLot" then trade must be forbidden;
2. statement "if (lots < MinLot) then lots = MinLot" is wrong;
3. this is one of general risk management rules and requirements.


Best regards,
Ais

 
Ais:

Hi again,


MathRound () should be used especially and exactly for zero returning, because:
1. if "lots < MinLot" then trade must be forbidden;
2. this is one of general risk management rules and requirements.


Best regards,
Ais

Yes, but.... We don't know the full context, but this function looks as though it's being used to calculate how much of a trade to close. The answer "zero; don't close anything" seems very unlikely to be useful in this context. I can't think why you'd use MathCeil() in the first place unless you wanted a function which always returned something other than zero. Though I could well be wrong.


(The next bit *added* following an edit to your comment while I was wriing the above response:)


2. statement "if (lots < MinLot) then lots = MinLot" is wrong;

Why would such a statement be so categorically wrong? There are plenty of contexts - and this may or may not be one of them - where the primary concern is to close at least some part of the open position. If it turns out that "closing 25%" actually gives you a choice between closing nothing and closing everything, there are plenty of contexts where you'd choose everything.

 

Hi Jjc,

Risk management usually must to forbid "somethings" and turn them to zero.

Lot size computing is one of primary subjects of risk management.

Best regards,

Ais

 

For example, let "lots = 0.02" and "MinLot = 0.10", then "lots=MinLot" enlarges risk to 5 times of the allowed one.

 
Ais:

Risk management usually must to forbid "somethings" and turn them to zero.

"Usually" isn't the same as your categorical "wrong" earlier. I wouldn't necessarily disagree with "usually".


For example, let "lot = 0.02" and "MinLots = 0.10", then "lot=MinLots" enlarges risk to 5 times of the allowed one.

Do you know that it's as simple as that?. Since we're talking here about single orders rather than the net position on multiple orders, in this example minlot must be greater than lotstep for the order to have been partially closed down to 0.02. This leads to ambiguities which aren't covered in the MetaTrader documentation. I've never experienced this in real life, and I've only seen one example given on this forum: 'Close Percentage of Trade Calculation'. There are three possible broker behaviours if minlot > lotstep (described in my post on that topic) - are you saying you know which one a broker enforces?

 

JUST TO CLARIFY my function above is to return the percent lots of remaining "global" lots on order.


MathCeil is proper call to return the higher of two numbers of lots e.g. 25 would be _lots = 13 and global lots would get lots =12.


jjc,


Thanks so much for your explanations on the FPP, I am making ASSumptions in my code about precision that I SHOULD KNOW BETTER about. However the general programmer is not going to take these attributes into consideration. Again I reiterate the need ofr an article that explicitly addresses double arithmetic and relationships.


Furthermore, I agree that the Double Normalization should be pushed into the MathFunctions() where it makes sence. MQL5 is the perfect place to remedy this type of abnormality.


John


PS

I am assuming that NormalizeDouble is far cheaper than MathFloor(56.000000000000007).

Reason: