You have to check your broker's volume conditions ...
Forum on trading, automated trading systems and testing trading strategies
How to calculate lots using multiplier according to number of opened orders?
Fernando Carreiro, 2017.09.01 21:57
Don't use NormalizeDouble(). Here is some guidance (code is untested, just serves as example):
// Variables for Symbol Volume Conditions double dblLotsMinimum = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MIN ), dblLotsMaximum = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MAX ), dblLotsStep = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_STEP ); // Variables for Geometric Progression double dblGeoRatio = 2.8, dblGeoInit = dblLotsMinimum; // Calculate Next Geometric Element double dblGeoNext = dblGeoInit * pow( dblGeoRatio, intOrderCount + 1 ); // Adjust Volume for allowable conditions double dblLotsNext = fmin( dblLotsMaximum, // Prevent too greater volume fmax( dblLotsMinimum, // Prevent too smaller volume round( dblGeoNext / dblLotsStep ) * dblLotsStep ) ); // Align to Step value
I just figured out that I had two problems. So, even with the long number it still accepts the quantity for ordering, so long as I round it.
Don't just round or normalise it to 2 digits. Do it the correct way as I have shown in the code above.
Floating-point has an infinite number of decimals, it's you, not understanding floating-point and that some numbers can't be represented exactly. (like 1/10.s)
Double-precision floating-point format - Wikipedia
See also The == operand. - MQL4 programming forum 2013.06.07
If you want to see the correct number of digits, convert it to a string with the correct/wanted accuracy.
question about decima of marketinfo() - MQL4 programming forum 2016.05.18
The function MathRound() has some accuracy problems.
Edit:
The function MathRound() has some accuracy problems if used as is without any correction for floating point roundoff errors.
The function NormalizeDouble() is better optimized for rounding of calculated prices/lots.
Here is a test script to show the accuracy issues and correct usage.
//double lotstep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double lotstep = 0.01; double lots = 2.405; //--- Round calculated lots to the nearest multiple of lotstep. double rounded_1 = NormalizeDouble(lots/lotstep, 0) * lotstep; //--- Less accurate rounding with edge numbers. double rounded_2 = MathRound(lots/lotstep) * lotstep; Print(lots); // output: 2.405 Print(rounded_1); // output: 2.41 Print(rounded_2); // output: 2.4 (Less accurate)
This is my function for the accurate rounding of lots.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double RoundVolume(double pVolume, string pSymbol = NULL) { pSymbol = pSymbol == NULL ? _Symbol : pSymbol; double minlot = SymbolInfoDouble(pSymbol, SYMBOL_VOLUME_MIN); double maxlot = SymbolInfoDouble(pSymbol, SYMBOL_VOLUME_MAX); double lotstep = SymbolInfoDouble(pSymbol, SYMBOL_VOLUME_STEP); if(minlot == 0 || maxlot == 0 || lotstep == 0) { Print(__FUNCTION__, ": error, cannot retrieve volume info for " + pSymbol); return (0); } const double Epsilon = 0.000001; pVolume -= minlot; if(pVolume < -Epsilon) { return (0); } if(pVolume < Epsilon) { return (minlot); } //--- Round calculated lots to the nearest multiple of lotstep. pVolume = minlot + NormalizeDouble(pVolume/lotstep, 0) * lotstep; if(pVolume > maxlot) { pVolume = maxlot; } return (pVolume); }
No, MathRound is not less accurate. There is a rounding rule which states that when the digit is a 5, it should be rounded to the "even" number instead of the "odd" number. It is NormalizeDouble that is not working correctly by not applying this rule of rounding.
When rounding, you examine the digit following (i.e., to the right of) the digit that is to be the last digit in the rounded off number. The digit you are examining is the first digit to be dropped.
- If that first digit to be dropped is less than 5 (that is, 1, 2, 3 or 4), drop it and all the digits to the right of it.
- If that first digit to be dropped is more than 5 (that is, 6, 7, 8 or 9), increase by 1 the number to be rounded, that is, the preceding figure (to the digit being dropped).
- If that first digit to be dropped is 5, round the digit that is to rounded off so that it will be even. Keep in mind that zero is considered to be even when rounding off.
So in this case, the correct rounding for 2.405 is 2.40, not 2.41 (in accordance to above rules).
However, the above points are moot, because as a "double", 2.405 would actually be represented as "2.40499999999999980460074766597245", and ( 2.405 / 0.01 ), is represented as "240.49999999999997157829056959599257", so NormalizeDouble is totally incorrect!
Dear Fernando Carreiro,
I don't totally agree with your explanation. Given arbitrary values for lots (for example 2.085, 2.175, 2.405), you cannot predict the output of decimal rounding with MathRound().
Try to round those values (to a step of 0.01 lot) in your head before looking at the code and compare your results.
Here is a script to further explain the inconstancies of MathRound():
Edit:
Round3 that applies (1+DBL_EPSILON) to MathRound() has well predicted results. It follows the 'Round half away from zero' rule and it gives exactly the same output as NormalizeDouble() for any floating point number.
#define Round1(lots, lotstep) (NormalizeDouble(lots/lotstep, 0) * lotstep) #define Round2(lots, lotstep) (MathRound(lots/lotstep) * lotstep) #define Round3(lots, lotstep) (MathRound(lots/lotstep*(1+DBL_EPSILON)) * lotstep) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnStart() { //double lotstep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double lotstep = 0.01; //--- NormalizeDouble() always gives a predictable output Print("lots = 2.085, Round1() = ", Round1(2.085, lotstep)); // output: 2.09 (Round half away from zero) Print("lots = 2.175, Round1() = ", Round1(2.175, lotstep)); // output: 2.18 (Round half away from zero) Print("lots = 2.405, Round1() = ", Round1(2.405, lotstep)); // output: 2.41 (Round half away from zero) Print("-----------"); //--- MathRound() has an inconsistent rounding mode Print("lots = 2.085, Round2() = ", Round2(2.085, lotstep)); // output: 2.09 (Round half away from zero) Print("lots = 2.175, Round2() = ", Round2(2.175, lotstep)); // output: 2.17 (Round half to odd) Print("lots = 2.405, Round2() = ", Round2(2.405, lotstep)); // output: 2.4 (Round half to even) Print("-----------"); //--- Fix to MathRound() to follow 'Round half away from zero' rule Print("lots = 2.085, Round3() = ", Round3(2.085, lotstep)); // output: 2.09 (Round half away from zero) Print("lots = 2.175, Round3() = ", Round3(2.175, lotstep)); // output: 2.18 (Round half away from zero) Print("lots = 2.405, Round3() = ", Round3(2.405, lotstep)); // output: 2.41 (Round half away from zero) } //+------------------------------------------------------------------+ /* output: lots = 2.085, Round1() = 2.09 lots = 2.175, Round1() = 2.18 lots = 2.405, Round1() = 2.41 ----------- lots = 2.085, Round2() = 2.09 lots = 2.175, Round2() = 2.17 lots = 2.405, Round2() = 2.4 ----------- lots = 2.085, Round3() = 2.09 lots = 2.175, Round3() = 2.18 lots = 2.405, Round3() = 2.41 */
NormalizeDouble() always follows a consistent rounding mode (Round half away from zero) because it is optimized to handle floating-point roundoff errors.
And, to clarify things, any of MathRound() / NormalizeDouble() can be used to round decimal numbers (although in different ways), so that the trading server will not complain.
The main issue against using MathRound() for decimal rounding is inconsistent results and poor code predictability.
Edit:
I mean using MathRound() without (1+DBL_EPSILON) for decimal rounding has inconsistent results because it does not follow any well-known rounding rule.
By the way, in MetaTrader 5 the function DoubleToString() uses internally the same rounding rule as NormalizeDouble().
Edit: the rule is 'Round half away from zero'
void OnStart() { int digits = 2; //--- NormalizeDouble() follows 'Round half away from zero' rule Print("number = 2.085, NormalizeDouble() = ", NormalizeDouble(2.085, digits)); Print("number = 2.175, NormalizeDouble() = ", NormalizeDouble(2.175, digits)); Print("number = 2.405, NormalizeDouble() = ", NormalizeDouble(2.405, digits)); Print("-----------"); //--- DoubleToString() follows 'Round half away from zero' rule Print("number = 2.085, DoubleToString() = ", DoubleToString(2.085, digits)); Print("number = 2.175, DoubleToString() = ", DoubleToString(2.175, digits)); Print("number = 2.405, DoubleToString() = ", DoubleToString(2.405, digits)); } /* output: number = 2.085, NormalizeDouble() = 2.09 number = 2.175, NormalizeDouble() = 2.18 number = 2.405, NormalizeDouble() = 2.41 ----------- number = 2.085, DoubleToString() = 2.09 number = 2.175, DoubleToString() = 2.18 number = 2.405, DoubleToString() = 2.41 */
Rounding Modes:
Fixes for decimal rounding with MathRound()
https://stackoverflow.com/a/48764436
My best regards,
Looks like you may be right. I had originally believed MathRound to be following the "Round to Even" convention (like in C#), but its not. I decided to lookup the "C" convention, which states the following rule:
- If decimal value is from ”.1 to .5″, it returns integer value less than the argument. If decimal value is from “.6 to .9″, it returns the integer value greater than the argument.
However, looking at the output, its NOT following that convention either. I guess it is following another convention, but don't see which one. As you said, it does seem unpredictable, but I am sure it is following some convention but we just don't know which one.
For precision's sake, I outputted all the values and intermediate steps in high precision and I got the following:
Value 2.085 is represented as 2.08499999999999996447286321199499070644378662109375000000 Value 208.500 is represented as 208.50000000000000000000000000000000000000000000000000000000 NormalizeDouble( 208.500 ) = 209.00000000000000000000000000000000000000000000000000000000 MathRound( 208.500 ) = 209.00000000000000000000000000000000000000000000000000000000 MathRound EPSILON ( 208.500 ) = 209.00000000000000000000000000000000000000000000000000000000 Round1 = 2.08999999999999985789145284797996282577514648437500000000 Round2 = 2.08999999999999985789145284797996282577514648437500000000 Round3 = 2.08999999999999985789145284797996282577514648437500000000 Value 2.175 is represented as 2.17499999999999982236431605997495353221893310546875000000 Value 217.500 is represented as 217.49999999999997157829056959599256515502929687500000000000 NormalizeDouble( 217.500 ) = 218.00000000000000000000000000000000000000000000000000000000 MathRound( 217.500 ) = 217.00000000000000000000000000000000000000000000000000000000 MathRound EPSILON ( 217.500 ) = 218.00000000000000000000000000000000000000000000000000000000 Round1 = 2.18000000000000015987211554602254182100296020507812500000 Round2 = 2.16999999999999992894572642398998141288757324218750000000 Round3 = 2.18000000000000015987211554602254182100296020507812500000 Value 2.405 is represented as 2.40499999999999980460074766597244888544082641601562500000 Value 240.500 is represented as 240.49999999999997157829056959599256515502929687500000000000 NormalizeDouble( 240.500 ) = 241.00000000000000000000000000000000000000000000000000000000 MathRound( 240.500 ) = 240.00000000000000000000000000000000000000000000000000000000 MathRound EPSILON ( 240.500 ) = 241.00000000000000000000000000000000000000000000000000000000 Round1 = 2.41000000000000014210854715202003717422485351562500000000 Round2 = 2.39999999999999991118215802998747676610946655273437500000 Round3 = 2.41000000000000014210854715202003717422485351562500000000
Sorry, but I am not quite following you! What convention is it using exactly?
"rounding rule to round to whole integers" is not a convention, but just what its function is.
As for trying to adjust for representation or round-off errors, I personally prefer that a rounding functions DOES NOT try to do that.
The only thing I am not clear from your statement, is what convention is used by "MathRound". Its not the "C" convention, nor is it the "Round to Even" convention!
Do you know which convention it uses?
EDIT: Looks like you edited your post after I answered. So its using the "Round half away from zero". Thanks for that info!
EDIT2: The reason, I like having the rounding NOT adjust for corrections, is because I calculate my lots based on self-adjusting fractional risk percentages, so I prefer that it does not try to adjust. That is my preference obviously, not that of other traders.
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
I can't place an order because I get an error that there's too many digits in the quantity.
I really hope someone can help me with this, because I can't run my algo at all whilst I have this persistent problem.
For example, if I try double thisQty=(double)"0.13";
I always get 0.130000000000000004
Here's what I've tried:
double thisQty=0.130000000000000004; // This is the value I get to begin with.
thisQty=NormalizeDouble(thisQty,2);
string strQty=DoubleToString(thisQty,2);
thisQty=(double)strQty;
thisQty=round(thisQty*100)/100;
thisQty=(MathFloor(thisQty * 100)) / 100;
thisQty=MathRound(thisQty*100)/100;