Rblpapi icon indicating copy to clipboard operation
Rblpapi copied to clipboard

bdh request does not return data frame because of a single bad date in Bloomberg data

Open philaris opened this issue 6 years ago • 0 comments

Hi all, and thank you for this nice API.

There is an issue with the following historical request for ticker 'RBS LN Equity' and field LATEST_ANNOUNCEMENT_DATE:

Rblpapi::blpConnect()
result <- Rblpapi::bdh(
  securities = c('RBS LN Equity'),
  fields = c('LATEST_ANNOUNCEMENT_DT'),
  start.date = as.Date('2003-12-01'),
  end.date = as.Date('2018-11-30'),
  options = NULL,
  overrides = c('FUND_PER' = 'Q'),
  verbose = TRUE
)

I get the following error:

Day of month value is out of range 1..31
<boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::gregorian::bad_day_of_month> > in bdh_Impl(con, securities, fields, start.date, end.date, options, overrides, verbose, identity, int.as.double): Day of month value is out of range 1..31>

Data in the LATEST_ANNOUNCEMENT_DT field is received as a real number, where for example 2018-10-26 is represented by the real number 20181026.000000.

The reason for the error is the following entry in the data from Bloomberg as seen from the verbose output of the above request:

fieldData = {
  date = 2015-12-31
  LATEST_ANNOUNCEMENT_DT = 0.000000
}

All other entries for LATEST_ANNOUNCEMENT_DT in the timeseries are fine (stored as real number values), e.g.:

fieldData = {
  date = 2018-09-30
  LATEST_ANNOUNCEMENT_DT = 20181026.000000
}

I traced the exception through HistoricalDataResponseToDF in src/bdh.cpp:

            populateDfRow(res[colindex], i, e, rtypes[colindex]);

and then populateDfRow in src/blpapi_utils.cpp:

  case RblpapiT::Date:
    // handle the case of BBG passing down dates as double in YYYYMMDD format
    REAL(ans)[row_index] = e.datatype()==BLPAPI_DATATYPE_FLOAT32 || e.datatype()==BLPAPI_DATATYPE_FLOAT64 ?
      bbgDateToRDate(e.getValueAsFloat64()) :
      bbgDateToRDate(e.getValueAsDatetime());
    break;

and bbgDateToRDate in src/blpapi_utils.cpp:

const int bbgDateToRDate(const Datetime& bbg_date) {
  if(bbg_date.hasParts(DatetimeParts::TIME)) {
    throw std::logic_error("Attempt to convert a Datetime with time parts set to an R Date.");
  }
  const boost::gregorian::date r_epoch(1970,1,1);
  boost::gregorian::date bbg_boost_date(bbg_date.year(),bbg_date.month(),bbg_date.day());
  boost::gregorian::date_period dp(r_epoch,bbg_boost_date);
  return static_cast<int>(dp.length().days());
}

const int bbgDateToRDate(const double yyyymmdd_date) {
  if(yyyymmdd_date < 0) {
    throw std::logic_error("Attempt to convert a negative double value to an R Date.");
  }
  if(trunc(yyyymmdd_date)!=yyyymmdd_date) {
    throw std::logic_error("Attempt to convert a double value with time parts set to an R Date.");
  }

  const boost::gregorian::date r_epoch(1970,1,1);
  const int year = static_cast<int>(yyyymmdd_date/1.0e4);
  const int month = static_cast<int>(yyyymmdd_date/1.0e2) % 100;
  const int day = static_cast<int>(yyyymmdd_date) % 100;
  boost::gregorian::date bbg_boost_date(year,month,day);
  boost::gregorian::date_period dp(r_epoch,bbg_boost_date);
  return static_cast<int>(dp.length().days());
}

The exception is thrown in the constructor call for variable bbg_boost_date in the above code. With a single problematic value as above, the Rblpapi::bdh call returns no R data frame. My question is whether this behavior is too strict.

I am wondering if a better behavior would be to ignore the exceptions that are propagated through populateDfRow and have an NA value in the resulting R data frame.

A simple (but drastic) solution that implements this last behavior and does not change the existing code too much could be to create a version of populateDfRow (e.g., called populateDfRowNoExcept) that catches all exceptions and use that in HistoricalDataResponseToDF of src/bdh.cpp.

What do you think?

Best regards, Panagiotis CHEILARIS

philaris avatar Dec 07 '18 18:12 philaris