pythonbloombergblpapi

How to use the BQL Bloomberg excel formula for python API (xbbg or blpapi)?


Is there a way to use the BQL-formula in Python in the BLPAPI or XBBG API's instead of looping through a bunch of tickers to retrieve data on i.e. all of the stocks of the S&P500 using a BDP or BDS formula? (This will quickly reach the data limit for the day, I suspect, since I want to check a bunch of different indicies).

I found a post from 2019, where BQNT was suggested, but I would prefere to avoid using BQNT, link here: How to implement BQL Bloomberg excel formula to python API (blpapi)?.

Thanks in advance!


Solution

  • Further to the comments, I played around with a proof-of-concept for driving Excel from Python. This quick'n'dirty script opens Excel in the background, puts a BQL formula in a cell, polls for a return value, and fills a DataFrame:

    import pandas as pd
    import time
    
    import win32com.client as wc
    
    #Get a dispatch interface for the Excel app
    _xl = wc.Dispatch("Excel.Application")
    #Ensure the Bloomberg addin is loaded
    _xl.Workbooks.Open('c:\\blp\\API\\Office Tools\\BloombergUI.xla')
    
    #Create a new workbook
    wb = _xl.Workbooks.Add()
    ws = wb.Sheets(1)
    cl = ws.Cells(1,1) #Cell A1 on Sheet 1
    
    #Define BQL query, and set cell formula
    qry ='=@BQL.Query("get(YIELD) for(filter(bonds([\'IBM US Equity\']),CPN_TYP==Fixed and CRNCY==USD))")' 
    cl.Formula=qry
    _xl.Calculate()
    
    #Check the cell's value: it will likely be #N/A ...
    res = cl.Value
    nLoop = 0
    nTimeout = 100 #ie 10 seconds
    
    #Loop until either get a non-# return or timeout
    while res[0]=='#' and nLoop<=nTimeout:
        time.sleep(0.1) #100 ms
        res = cl.Value
        nLoop += 1
    
    if res[0] == '#':
        print('Timed out')
        return
    
    print('Results after {0:} secs'.format(nLoop/10.0))
    
    #The Bloomberg addin will have changed the original BQL formula
    #and added a 'cols=x,rows=y' parameter at the end
    #This tells us the size of the data
    #as BQL doesn't seem to have the option to return a dynamic array
    f = cl.Formula
    rc = f.split(',')[-1].split(';')
    cols  = int(rc[0].split('=')[1])
    s = rc[1].split('=')[1]
    rows = int(s[0:len(s)-2])
    
    #Retrieve the values from this new range
    data = ws.Range(cl,ws.Cells(rows,cols)).Value
    
    #Convert to DataFrame
    df=pd.DataFrame(data[1:],columns=data[0])
    print(df)
    
    #Tidy up
    _xl.DisplayAlerts = False
    wb.Close()  
    _xl.Quit()
    

    Output:

    Results after 1.4 secs
                   ID     YIELD
    0   DD103619 Corp  1.012017
    1   BJ226366 Corp  1.921489
    2   DD103620 Corp  3.695580
    3   ZS542668 Corp  2.945504
    4   BJ226369 Corp  2.899166
    5   ZS542664 Corp  1.109456
    6   BJ226365 Corp  1.350594
    7   ZS542666 Corp  2.732168
    8   ZS542661 Corp  0.147570
    9   ZS542663 Corp  0.621825
    10  EJ772545 Corp  0.391708
    11  EJ222340 Corp  2.846866
    12  ZS542665 Corp  1.842695
    13  EJ299219 Corp  0.224708
    14  DD108917 Corp  3.733077
    15  AM269440 Corp  0.189621
    16  QJ633474 Corp  0.295588
    17  BJ226367 Corp  2.727445
    18  EC767655 Corp  2.241108
    19  EI062653 Corp  2.728811
    20  JK138051 Corp  1.077776
    21  DD115180 Corp  1.604258
    22  DD112334 Corp  1.527195
    23  EK063561 Corp  0.570778
    24  AM269866 Corp  1.329918
    25  JK138053 Corp  2.915085
    26  EH589075 Corp  3.110513
    

    If I were to do this in production, I'd wrap the whole thing in a class to avoid stopping and starting Excel each time I wanted to perform a Query. Also, I haven't tested what happens in the user is already running Excel for something else!