Abstract
I know Bloomberg has made it's API open to the public. Unfortunately, I use Python and there begins my dilemma. It is not a Bloomberg supported programming language. Speaking of which, after any convesation I have with someone from their Helpdesk, there's nothing much I can do but bang my head on top of my desk.I have written this document to provide ample information on how to interface with Bloomberg using Python. I have endured a great amount of pain in order to make sure that information here will work for all Bloomberg installations.
Bloomberg.Data.1
This is the legacy way of doing things. It DOESN'T work on all Bloomberg installations. But for completeness sake, the code is as simple as:import win32com.client BLP = win32com.client.Dispatch('Bloomberg.Data.1')
V3 API
comtypes, win32com, and ctypes are 3 python modules that I have experimented with. I will only provide information for comtypes and win32com since my tango with ctypes has gone into an infinite loop. According to Bloomberg, the library for V3 API can be accessed via registered or unregistered mode. The modules below will determine which one you have. I have broken down the modules for easy maintenance.bbg_helper.py
import logger import win32com.client, win32com.client.gencache import comtypes.client as COM import blpapicom2_unregistered as unreg_mode try: logger.info('Trying blpapicom2.Session.1 registered mode (comtypes.client)') session = COM.CreateObject('blpapicom2.Session.1') logger.info('comtypes.client blpapicom2.Session.1 registered mode is OK') except Exception as e: logger.warn('FAILED IN STARTING blpapicom2.Session.1 WITH MESSAGE: %s', str(e)) try: logger.info('Trying blpapicom.Session registered mode (comtypes.client)') session = COM.CreateObject('blpapicom.Session') logger.info('comtypes.client blpapicom.Session registered mode is OK') except Exception as e: logger.warn('FAILED IN STARTING blpapicom.Session WITH MESSAGE: %s', str(e)) try: logger.info('Trying blpapicom2.Session.1 registered mode (win32com.client)') session = win32com.client.CastTo(win32com.client.gencache.EnsureDispatch('blpapicom2.Session.1'),'ISession') logger.info(win32com.client blpapicom2.Session.1 registered mode is OK) except Exception as e: logger.warn('FAILED IN STARTING blpapicom2.Session.1 WITH MESSAGE: %s', str(e)) try: logger.info('Trying blpapicom.Session registered mode (win32com.client)') session = win32com.client.CastTo(win32com.client.gencache.EnsureDispatch('blpapicom.Session'),'ISession') logger.info('win32com.client blpapicom.Session registered mode is OK') except Exception as e: ) logger.warn('FAILED IN STARTING blpapicom.Session WITH MESSAGE: %s', str(e)) try: session = unreg_mode.BlpCoCreateInstance() except: pass
blpapicom2_unregistered.py
import sys import win32com.client import comtypes.client as COM import logger import blpapicom_unregistered as unreg_mode # config for Windows XP users API_XP = unreg_mode.API2_XP # config for Windows 7 users API_WIN7 = unreg_mode.API2_WIN7 def BlpCoCreateInstance(): ''' Return a Session object ''' Session = None Module = None try: logger.info('Trying blpapicom2.dll un-registered mode (comtypes.client)') if sys.getwindowsversion()[0] < 6: logger.info('Trying for Windows XP config') Module = COM.GetModule(API_XP) else: logger.info('Trying for Windows 7 config') Module = COM.GetModule(API_WIN7) if Module is not None: Session = COM.CreateObject(Module.Session) logger.info('comtypes.client blpapicom2.dll un-registered mode is OK') except Exception as e: logger.warn('FAILED IN STARTING blpapicom2.dll WITH MESSAGE: %s', str(e)) try: logger.info('Trying blpapicom2.dll un-registered mode (win32com.client)') Session = win32com.client.CastTo(win32com.client.Dispatch('blpapicom2.Session'),'ISession') logger.info('win32com.client blpapicom2.dll un-registered mode is OK') except Exception as e: logger.warn('FAILED IN STARTING blpapicom2.dll WITH MESSAGE: %s', str(e)) try: Session = unreg_mode.BlpCoCreateInstance() except: pass return Session
blpapicom_unregistered.py
import sys import win32com.client import comtypes.client as COM import logger import ctypes_unregistered as unreg_mode # config for Windows XP users API2_XP = unreg_mode.API2_XP API_XP = unreg_mode.API_XP # config for Windows 7 users API2_WIN7 = unreg_mode.API2_WIN7 API_WIN7 = unreg_mode.API_WIN7 def BlpCoCreateInstance(): ''' Return a Session object ''' Session = None Module = None try: logger.info('Trying blpapicom.dll un-registered mode (comtypes.client)') if sys.getwindowsversion()[0] < 6: logger.info('Trying for Windows XP config') Module = COM.GetModule(API_XP) else: logger.info('Trying for Windows 7 config') Module = COM.GetModule(API_WIN7) if Module is not None: Session = COM.CreateObject(Module.Session) logger.info('comtypes.client blpapicom.dll un-registered mode is OK') except Exception as e: logger.warn('FAILED IN STARTING blpapicom.dll WITH MESSAGE: %s', str(e)) try: logger.info('Trying blpapicom.dll un-registered mode (win32com.client)') Session = win32com.client.CastTo(win32com.client.Dispatch('blpapicom.Session'),'ISession') logger.info('win32com.client blpapicom.dll un-registered mode is OK') except Exception as e: try: Session = unreg_mode.BlpCoCreateInstance() except: pass return Session
ctypes_unregistered.py
import sys import ctypes import logger OLEAUT32 = ctypes.oledll.oleaut32 OLE32 = ctypes.oledll.ole32 OLE32.CoInitialize(0) # IIDs RCLSID = '{9FDBE237-38A5-4d8c-BB9C-2EB55FB1EABE}' RIID = '{4AC751C2-BB10-4702-BB05-791D93BB461C}' BYTE = ctypes.c_byte WORD = ctypes.c_int DWORD = ctypes.c_long # config for Windows XP users ROOT_XP = r'C:\Program Files\BLP\API' LOADER_XP = ROOT_XP + '\\' + 'bbloaderv3.dll' API2_XP = ROOT_XP + '\\' + 'blpapicom2.dll' API_XP = ROOT_XP + '\\' + 'blpapicom.dll' # config for Windows 7 users ROOT_WIN7 = r'C:\Program Files (x86)\BLP\API' LOADER_WIN7 = ROOT_WIN7 + '\\' + 'bbloaderv3.dll' API2_WIN7 = ROOT_WIN7 + '\\' + 'blpapicom2.dll' API_WIN7 = ROOT_WIN7 + '\\' + 'blpapicom.dll' def PrintGUIDValue(guid): ''' Return GUID _fields_ values ''' return "Data1: %s; Data2: %s; Data3: %s; Data4: %s" %(str(guid.Data1), str(guid.Data2), str(guid.Data3),"".join(str(guid.Data4))) class GUID(ctypes.Structure): _fields_ = [ ("Data1", ctypes.c_long), ("Data2", ctypes.c_int), ("Data3", ctypes.c_int), ("Data4", ctypes.c_byte * 8), ] def __init__(self, name=None): if name is not None: self._as_parameter = OLE32.CLSIDFromString(unicode(name), ctypes.byref(self)) def __repr__(self): s = (ctypes.c_wchar * 39)() OLE32.StringFromGUID2(ctypes.byref(self), s, 39) return "guid: %s" % s.value def __str__(self): s = (ctypes.c_wchar * 39)() OLE32.StringFromGUID2(ctypes.byref(self), s, 39) return s.value def __cmp__(self, other): if isinstance(other, GUID): return not OLE32.IsEqualGUID(ctypes.byref(self), ctypes.byref(other)) return -1 def __nonzero__(self): result = str(buffer(self)) != "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" return result def __eq__(self, other, IsEqualGUID=OLE32.IsEqualGUID): return isinstance(other, GUID) and IsEqualGUID(ctypes.byref(self), ctypes.byref(other)) def PrintGUIDValue(self): ''' Return GUID _fields_ values ''' return PrintGUIDValue(self) def BlpCoCreateInstance(): ''' Implementation is incomplete. DO NOT USE! ''' Session = None bbloaderv3 = None blpapicom2 = None if sys.getwindowsversion()[0] < 6: bbloaderv3 = ctypes.windll.LoadLibrary(LOADER_XP) blpapicom2 = API_XP else: bbloaderv3 = ctypes.windll.LoadLibrary(LOADER_ISPACE) blpapicom2 = API2_WIN7 rclsidguid = GUID(RCLSID) riidguid = GUID(RIID) rclsid = ctypes.byref(rclsidguid) riid = ctypes.byref(riidguid) logger.info(repr(rclsidguid) + " := " + rclsidguid.PrintGUIDValue()) logger.info(repr(riidguid) + " := " + riidguid.PrintGUIDValue()) BlpCoCreateInstance = getattr(bbloaderv3, 'BlpCoCreateInstance') BlpCoCreateInstance.restype = ctypes.c_char_p bbloaderv3.BlpCoCreateInstance(blpapicom2, rclsid, riid) return Session """ We are trying to mimic how Bloomberg uses the data control in unregistered mode (VBA). Below is taken from their WAPI documentation Ref# COMv3RegUnreg ' Please note that the full path must be included to the location of the ' bbloaderv3.dll file. If this path is not correct, please, replace it ' with the correct path Private Declare Function BlpCoCreateIntance Lib "bbloaderv3.dll" _ (ByVal lpszFilePathName As String, ByRef rclsid As GUID, riid As GUID) As Object Private Declare Function CLSIDFromString Lib "ole32.dll" (pstCLS As Long, _ clsid As GUID) As Long Private Declare Function GetActiveObject Lib "oleaut32.dll" (lpRclsid As Long, _ pvReserved As Long, lpUnk As Long) As Long Private Type GUID Data1 As Long Data2 As Integer Data3 As Integer Data4 (7) As Byte End Type Dim rclsid As GUID Dim riid as GUID Dim FileName As String Private WithEvents session As Object Private Function GuidFromStGuid(ByVal stGuid As String) As GUID Dim rc As Long If Left$(stGuid, 7) = "{guid {" Then If Right$(stGuid, 2) = "}}" Then stGuid = Mid$(stGuid, 7, 38) End If End If rc = CLSIDFromString(ByVal StrPtr(stGuid), GuidFromStGuid) End Function ' Instantiate the Bloomberg COM Data Control Private Sub Class_Initialize() FileName = "blpapicom2.dll" Set session = BlpCoCreateInstance(FileName, _ GuidFromStGuid("{guid {9FDBE237-38A5-4d8c-BB9C-2EB55FB1EABE}}"), _ GuidFromStGuid("{guid {4AC751C2-BB10-4702-BB05-791D93BB461C}}")) session.Start End Sub """
Session
The ultimate goal is to get a Session object. Once that is done, sending request should be a breeze.import datetime TODAY = datetime.datetime.today() APIREFDATA_SVC = '//blp/refdata' RESPONSE = 5 PARTIAL_RESPONSE = 6 def MakeRequest(security, fldList, overrideFields=None, overrideValues=None): ''' Request test harness ''' result = [] session.QueueEvents = True session.Start() session.OpenService(APIREFDATA_SVC) service = session.GetService(APIREFDATA_SVC) req = service.CreateRequest("ReferenceDataRequest") #not sure if this will work sec = req.GetElement('securities') sec.AppendValue(security) flds = req.GetElement('fields') for fld in fldList: flds.AppendValue(fld) if overrideFields: overridables = zip(overrideFields, overrideValues) overrides = req.GetElement("overrides") for overrideField, overrideValue in overridables: override = overrides.AppendElment() override.SetElement('fieldId', overrideField) override.SetElement('value', overrideValue) session.SendRequest(req) while True: eventObj = session.NextEvent() if eventObj.EventType == PARTIAL_RESPONSE or eventObj.EventType == RESPONSE: it = eventObj.CreateMessageIterator() while it.Next(): msg = it.Message numSecurities = msg.GetElement("securityData").NumValues for secIndex in xrange(0, numSecurities): security = msg.GetElement("securityData").GetValue(secIndex) fields = security.GetElement("fieldData") numFields = fields.NumElements for fldIndex in xrange(0, numFields): field = fields.GetElement(fldIndex).Value result.append(field) if eventObj.EventType == RESPONSE: break return result def MakeHistoricalDataRequest(security, fldList, overrideFields=None, overrideValues=None, dateFrom=None, dateTo=None): ''' Historical request test harness ''' result = [] session.QueueEvents = True session.Start() session.OpenService(APIREFDATA_SVC) service = session.GetService(APIREFDATA_SVC) req = service.CreateRequest("HistoricalDataRequest") sec = req.GetElement('securities') sec.AppendValue(security) flds = req.GetElement('fields') for fld in fldList: flds.AppendValue(fld) if dateFrom: req.Set('startDate', dateFrom) else: req.Set('startDate', TODAY.strftime('%Y%m%d')) if dateTo: req.Set('endDate', dateTo) else: req.Set('endDate', TODAY.strftime('%Y%m%d')) req.Set('periodicitySelection', 'DAILY') if overrideFields: overridables = zip(overrideFields, overrideValues) overrides = req.GetElement("overrides") for overrideField, overrideValue in overridables: override = overrides.AppendElment() override.SetElement('fieldId', overrideField) override.SetElement('value', overrideValue) session.SendRequest(req) while True: eventObj = session.NextEvent() if eventObj.EventType == PARTIAL_RESPONSE or eventObj.EventType == RESPONSE: it = eventObj.CreateMessageIterator() while it.Next(): msg = it.Message security = msg.GetElement("securityData") #secVal = security.GetElement("security") field_exceptions = security.GetElement("fieldExceptions") # process exceptions if field_exceptions.NumValues > 0: element = field_exceptions.GetValuesAsElement(0) field_id = element.GetElement("fieldId") error_info = element.GetElement("errorInfo") error_message = error_info.GetElement("message") logger.warn('Exception with %s with message: %s' %(field_id, error_message)) # process field data field_data = security.GetElement("fieldData") numFields = field_data.NumValues for fldIndex in xrange(0, numFields): element = field_data.GetValueAsElement(fldIndex) # repack everything by date subResult = [] for field in fldList: if element.HasElement(field): subResult.append(element.GetElement(field).Value) result.append(subResult) if eventObj.EventType == RESPONSE: break return result
Bloomberg Open API (BLPAPI) v3.5 was just released and includes a native Python SDK.
ReplyDeletehttp://www.openbloomberg.com/2012/11/21/open-api-blpapi-v3-5-x-released/
Would you be interested in any bloomberg open api development work, for a project I am working on? Drop me an email knowlesi07@googlemail.com
ReplyDelete