CopySpread is not the same as ASK-BID... Am I missing something?

 

I had some random results trying to compare brokers and their spreads...

So I wrote a little code to see where my spread values were going wrong - see below.

Now I'm not sure at all! - the CopySpread function seems to be making it up!?
See the example output (suggesting that the spread is either 6 or 11) ...
Grabbing Ask and Bid from SymbolInfoDouble at least gets close, but has annoying rounding errors...
Adding in NormaliseDouble seems to get rid of some of them, but not all...?! - as per example output.

Does anyone have any idea why SPREAD is not equal to ASK - BID in MQL5?
It seems to be consistently smaller in fact.

I assume there isn't any good solution for these annoying pseudo-rounding errors either?  "ask = 0.7031500000000001" - even after allegedly rounding?!

Ignore my #define'd code Diag(A) 'function' - it is just a simple way to quickly Print(VarName,VarValue) to the logs....

Am I missing something??


#define Diag(A) Comment(#A + " = " + (string)A);Print(#A + " = " + (string)A);
//+------------------------------------------------------------------+
void OnStart()
  {
   int copySpread[]; ArraySetAsSeries(copySpread,true);
   double askMinusBid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK) - SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   double ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   CopySpread(_Symbol,PERIOD_CURRENT,0,1,copySpread);
   Diag(askMinusBid);
   Diag(ask);
   Diag(bid);
   Diag(copySpread[0]);
  }

This outputs...

2021.02.17 00:03:21.185    MicroTest (CADCHF,M5)    askMinusBid = 0.00011
2021.02.17 00:03:21.185    MicroTest (CADCHF,M5)    ask = 0.7031500000000001
2021.02.17 00:03:21.185    MicroTest (CADCHF,M5)    bid = 0.70304
2021.02.17 00:03:21.185    MicroTest (CADCHF,M5)    copySpread[0] = 6

 
Im not 100 percent sure but i think CopySpread returns the average spread in that period which could be H1 fo example but Ask and Bid is the current tick price not average
 
Khuman Bakhramirad:
Im not 100 percent sure but i think CopySpread returns the average spread in that period which could be H1 fo example but Ask and Bid is the current tick price not average
Thanks, I can't find anything in MQL5 Reference that gives me any more info... No idea, might do a few tests, but I can't see how its right just now...
 
  1. Spread is the maximum (IIRC) during the candle. Ask-Bid is the current value.

  2. mlbarnes: I assume there isn't any good solution for these annoying pseudo-rounding errors either?  "ask = 0.7031500000000001" - even after allegedly

    Floating-point has an infinite number of decimals, it's your not understanding floating-point and that some numbers can't be represented exactly. (like 1/10.)
              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

 
William Roeder:
  1. Spread is the maximum (IIRC) during the candle. Ask-Bid is the current value.

  2. Floating-point has an infinite number of decimals, it's your not understanding floating-point and that some numbers can't be represented exactly. (like 1/10.)
              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

Not ignoring you, just spending some time testing these and trying to figure out for sure what data they are returning.
My current plan is to completely avoid CopyRates.spread and CopySpread data and instead use CopyRates.Ask and CopyRates.bid to calculate my own spread info...
Thanks for your guidance!


Just looking at your rounding advice link too - thanks.
 
William Roeder:
  1. Spread is the maximum (IIRC) during the candle. Ask-Bid is the current value.

  2. Floating-point has an infinite number of decimals, it's your not understanding floating-point and that some numbers can't be represented exactly. (like 1/10.)
              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

Thanks again William...

I did some more tests, and your solution cerainly is tidier - e.g:

string bidString = DoubleToString(bid,_Digits); // Does a much more consistent job of rounding decimals!

I did read a lot of that wikipedia page you sent, but I can't quite figure out the mechanisms of all this, and I wouldn't ask you to try to get me up to speed!
The one thing I wanted to know is whether these FP system limitations mean that I can't even turn a number like the one above ( 0.7031500000000001 ) into a string and back then into a double to get rid of the trailing zeros (when Normalize doesn't)...
I answered my own question - see test code below... Fascinating!
I know that this is all a compromise to enable double variables to store enormous variations and all, so I can certainly live with it.
Thanks very much for helping me to at least understand the reason and implications, even if I don't fully understand how it works!

As far as the spread inconsistencies go, I haven't found an answer.

CopySpread usually gives a smaller value for spread than Ask-Bid does, so it can't be the maximum spread seen during the candle. Maybe the minimum??
Anyway, I'm going to just do my own maths (Ask-Bid) and avoid CopyRates and CopySpread data when trying to determine the current spread.

Thanks again! Martyn...


Here is my test code to illustrate Double Precision floating point limitations (hope someone else finds this helpful to understand) ...
My Diag 'function' just outputs the content variable's name and content to both the chart comments and expert output.

#define Diag(A) Comment(#A + " = " + (string)A);Print(#A + " = " + (string)A);  // add Diag(var); anywhere to Comment and Print the var name and value!

//+------------------------------------------------------------------+
void OnStart()
  {
  double test = 0.7031500000000001;
  double test2 = NormalizeDouble(test,5);
  string test3 = DoubleToString(test,5);
  double test4 = StringToDouble(test3);
  
  double test5 = 0.70315;        // An even simpler example!
  
  Diag(test);     // A troublesome number...
  Diag(test2);    // Not a solution...
  Diag(test3);    // Better!
  Diag(test4);    // ...but not when converted back again?
  Diag(test5);    // Even this is beyond the system's precision!
  }
//+------------------------------------------------------------------+

Output :

2021.02.22 17:59:47.798    MicroTest (McDonalds_Corporation_(MCD.N),M5)    test = 0.7031500000000001
2021.02.22 17:59:47.798    MicroTest (McDonalds_Corporation_(MCD.N),M5)    test2 = 0.7031500000000001
2021.02.22 17:59:47.798    MicroTest (McDonalds_Corporation_(MCD.N),M5)    test3 = 0.70315
2021.02.22 17:59:47.798    MicroTest (McDonalds_Corporation_(MCD.N),M5)    test4 = 0.7031500000000001
2021.02.22 17:59:47.798    MicroTest (McDonalds_Corporation_(MCD.N),M5)    test5 = 0.7031500000000001

 

All double numbers are correct. Read the wikipedia link again William posted.

As to spread. Why not just use 

SymbolInfoDouble(_Symbol,SYMBOL_SPREAD);

No need to calculate it yourself.

 
Enrique Dangeroux:

All double numbers are correct. Read the wikipedia link again William posted.

As to spread. Why not just use 

No need to calculate it yourself.

Aha! Another way... I shall check it out now... Thanks.

Checked : An even nicer solution, thanks.
I did need to adjust some syntax, as below - it is SymbolInfoInteger (which oddly yields a long!) - the below seems my best code candidate so far...
The MathPow adjustment and casting to double helps to get in on the same scale as price data like ask and bid... (is that the best way to do it all do you think?)...

double spread = (double)(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD)/MathPow(10,_Digits));

All good, thanks to all three of you for your help.

Martyn

Documentation on MQL5: Common Functions / GetTickCount64
Documentation on MQL5: Common Functions / GetTickCount64
  • www.mql5.com
GetTickCount64 - Common Functions - MQL5 Reference - Reference on algorithmic/automated trading language for MetaTrader 5
 
  1. x / MathPow(10,_Digits) is simply x * _Point
  2. No need for the cast. long / double is a double.
 
William Roeder:
  1. x / MathPow(10,_Digits) is simply x * _Point
  2. No need for the cast. long / double is a double.

I have much to learn!

You are a star... new simple and much better looking code....

double spread = SymbolInfoInteger(_Symbol,SYMBOL_SPREAD)*_Point;
 
mlbarnes #:

I have much to learn!

You are a star... new simple and much better looking code....

Dear people, sorry for this "blast from the past" but I would appreciate if someone very expert here gives the "final word" on the real specification of the language.

In mql5 there are several ways for fetching spread information from the client. I have made a very simple indicator for checking them live:

//+------------------------------------------------------------------+
//|                                                    FS_testSpread |
//| Checking how mql5 indicates spread in various ways.              |
//+------------------------------------------------------------------+

#property copyright    "FS 2024"
#property version      "1.00"
#property description  "Showing in real time how mql5 reports spread in different ways"

#property strict
#property indicator_chart_window
#property indicator_plots 0

MqlRates rates[1];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
{
  return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator de-init function                         |
//+------------------------------------------------------------------+  

void OnDeinit(const int reason)
{
  Comment("");  // Cleanup
  return;
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[]
                )
{
  string CommentString = "";

  double curr_bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
  double curr_ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
  double spread_calc = curr_ask - curr_bid;
  double spread_indi = spread[0];
  CopyRates(_Symbol, PERIOD_CURRENT, 0, 1, rates);
  double spread_rates = rates[0].spread;
  double spread_info = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
  
  CommentString+="-----------------------------------------------------------------\n";
  CommentString+="Bid = " + DoubleToString(curr_bid) +"\n";
  CommentString+="Ask = " + DoubleToString(curr_ask) +"\n";
  CommentString+="Spread calculated as ask-bid = " + DoubleToString(spread_calc) +"\n";
  CommentString+="Spread returned in the indicator buffer = " + DoubleToString(spread_indi) +"\n";
  CommentString+="Spread returned by CopyRates = " + DoubleToString(spread_rates) +"\n";
  CommentString+="Spread returned by SymbolInfo = " + DoubleToString(spread_info) +"\n";
  CommentString+="-----------------------------------------------------------------\n";

  Comment(CommentString);

  //--- return value of prev_calculated for next call
  return(rates_total);
}

Now, this is a screenshot of what you get out of this:

I left the stadard 8 digits precision in all these numbers just to be sure to see everything I would like to see.

It looks quite clear to me that the only mql5 function reporting the "real" spread given by the difference between Ask and Bid price is the SymbolInfoInteger(_Symbol, SYMBOL_SPREAD). May I ask mighty guys to give a definition of what

* the spread[] array in the OnCalculate call does actually contain?

* What is the spread returned by CopyRates?

* Whether I am a complete dumb and use both the OnCalculated returned spread or the CopyRates the wrong way? I am absolutely open to receive the due criticism!


Best regards and thanks!