how to reliably get current bid/ask?

 
As customary, many of us will use:
SymbolInfoDouble(NULL,SYMBOL_BID);

or

SymbolInfoDouble(NULL,SYMBOL_ASK);

or

MarketInfo(NULL,MODE_ASK);

and those will work ok most of the time, however, for some of us they return 0 while implemented in the Strategy Tester. I am presenting here an alternative that, I hope, will be more consistent(both live and testing) and that will be more suited to multicurrency situations. This has worked for me:

//+------------------------------------------------------------------+
//| SymbolInfoDouble2                                                      |
//+------------------------------------------------------------------+
/*
 Calculate latest received prices such as ask,bid...
*/
double SymbolInfoDouble2(string name, ENUM_SYMBOL_INFO_DOUBLE prop_id, ENUM_TIMEFRAMES ChartTimeframe=0)
{
 MqlRates rates[2];//rates[100]; //change this according to the situation but i think 100 bars is sufficient for this. 
 int copied=-1,i=0,h=0,StartPos=0,BarCount=1;
 bool result=false;
 string out,AppliedPrice="high";
 double data_val=0,currentspread=0,result_val=0; 
 copied=CopyRates(name,ChartTimeframe,StartPos,BarCount,rates); 
      //transfer copied bars to result array
     if(copied >= BarCount) 
     {  
      i = BarCount;
         while(BarCount > 0 && i > 0) //use a if statement here
         { 
          i--;
          out=IntegerToString(i)+" tick time:"+TimeToString(rates[i].time); 
             if(AppliedPrice == "open")
             {
              data_val = rates[i].open;
             }
             else if(AppliedPrice == "high")
             {
              data_val = rates[i].high;
             }
             else if(AppliedPrice == "low")
             {
              data_val = rates[i].low;
             }
             else if(AppliedPrice == "close")
             {
              data_val = rates[i].close;
             } 
          currentspread = (double)rates[i].spread;   
              
             //remember currentspread = ask-bid;              
             if(prop_id == SYMBOL_BID)
             {
              result_val = data_val;
             }  
             //therefore currentspread + bid = ask; 
             else if(prop_id == SYMBOL_ASK)
             {
              result_val = currentspread + data_val;
             }    
             else if(prop_id == SYMBOL_POINT)
             {
              //i don't need this, maybe someone can complete here
             }                                                             
         }   
      //Print("Bars copied: "+IntegerToString(copied));   
     } 
     else Print("SymbolInfoDouble2 failed to get historical data for the symbol ",name);
 return result_val;      
}
//+------------------------------------------------------------------+
//| End of SymbolInfoDouble2                                               |
//+------------------------------------------------------------------+


Example usage1:

double ask = SymbolInfoDouble2(NULL,SYMBOL_ASK); //variable "ask" now has the latest ask price that has been downloaded.
Just use SymbolInfoDouble2() the same way like SymbolInfoDouble(). Now there are more capabilities to add but this is sufficient for me.
 

It's recommended to use SymbolInfoTick to query current Bid and Ask. Not sure though if CopyRates will do it better, and it will be slower for sure.

    MqlTick tick;
    if(!SymbolInfoTick(_Symbol,tick)) { Print("no tick data available, error = ",GetLastError()); ExpertRemove(); return; }
    double Ask=tick.ask;
    double Bid=tick.bid;
 
lippmaje:

It's recommended to use SymbolInfoTick to query current Bid and Ask. Not sure though if CopyRates will do it better, and it will be slower for sure.

I just tested "SymbolInfoTick(_Symbol,tick)" and it gave me the same result as "SymbolInfoDouble2", so I guess I will have to agree with you that it might be quicker. 

 
SymbolInfoDouble(NULL,SYMBOL_BID);

or

SymbolInfoDouble(NULL,SYMBOL_ASK);

or

MarketInfo(NULL,MODE_ASK);
Don't use NULL.
  • You can use NULL in place of _Symbol only in those calls that the documentation specially says you can. iHigh does, iCustom does, MarketInfo does not. OrderSend does not.
  • Don't use NULL (except for pointers where you explicitly check for it.) Use _Symbol and _Period, that is minimalist as possible and more efficient.
  • Zero is the same as PERIOD_CURRENT which means _Period. Don't hard code numbers.
 

Interestingly, applying SymbolInfoTick on a stock I am requesting the current price from is still yielding a 0.

But since we're ambitious coders there's no surrendering, right? Adding the CopyBars method as the very last resort helped me fetching the stock price - finally.

Btw. MarketInfo seems to be MQL4, isn't it?

Anyways, thanks a lot for this valuable thread, gentlemen! 

#ifndef isZero
	#define isZero(x) (fabs(x) < 0.000000001)
#endif

// (Just in case someone wants the isEqual function as well..)
#ifndef isEqual
	#define isEqual(x,y) (fabs(x-y) < 0.000000001)
#endif


double GetCurrentPrice(ENUM_ORDER_TYPE orderType)
{       double currentPrice = 0;
        if (orderType == ORDER_TYPE_BUY) { currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); }
        else if (orderType == ORDER_TYPE_SELL) { currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); }
        else { currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); }
        
        if (isZero(currentPrice))
        {
                MqlTick tick;
                currentPrice = SymbolInfoTick(_Symbol, tick);
                if (orderType == ORDER_TYPE_BUY) { currentPrice = tick.ask; }
                else if (orderType == ORDER_TYPE_SELL) { currentPrice = tick.bid; }
        }
        if (isZero(currentPrice))
        {
                MqlRates bar[1]; 
                CopyRates(_Symbol, _Period, 0, 1, bar);
                currentPrice = bar[0].close;
        }
        return currentPrice;
}