通过Excel中的VBA从彭博社刷新BDH公式

通过Excel中的VBA从彭博社刷新BDH公式,vba,outlook-addin,bloomberg,Vba,Outlook Addin,Bloomberg,我目前正在运行一个宏,它在一个单元格中选择一个标记,粘贴到另一个单元格中,该标记链接到BDH公式。然后,它复制彭博带来的一个值,然后复制并粘贴到另一个单元格中。宏重复了20个代码,所以我把它放在for语句中,但当我运行宏时,它不会给彭博社“时间”刷新。你知道我怎么修吗?我已经尝试了application.wait、application.run等 这是我目前的代码: Sub Atualizar_Relatorio() Dim Cont As Integer 'Dim Ticker As Str

我目前正在运行一个宏,它在一个单元格中选择一个标记,粘贴到另一个单元格中,该标记链接到BDH公式。然后,它复制彭博带来的一个值,然后复制并粘贴到另一个单元格中。宏重复了20个代码,所以我把它放在for语句中,但当我运行宏时,它不会给彭博社“时间”刷新。你知道我怎么修吗?我已经尝试了application.wait、application.run等

这是我目前的代码:

Sub Atualizar_Relatorio()

Dim Cont As Integer
'Dim Ticker As String
Cont = 0

  For Cont = 0 To 20

    ' Copy and paste the ticker in the cell where bloomber formula is linked
    
    ThisWorkbook.Sheets("Report").Cells(4 + Cont, 4).Copy
    ThisWorkbook.Sheets("Historical").Cells(17, 2).PasteSpecial Paste:=xlPasteValues
     
    ' Copy the values from BDH and paste in the other worksheet
    ThisWorkbook.Sheets("Historical").Cells(19, 4).Copy
    ThisWorkbook.Sheets("Report").Cells(4 + Cont, 5).PasteSpecial Paste:=xlPasteValues
    ThisWorkbook.Sheets("Report").Cells(4 + Cont, 20).Copy
    ThisWorkbook.Sheets("Report").Cells(4 + Cont, 7).PasteSpecial Paste:=xlPasteValues
    ThisWorkbook.Sheets("Historical").Cells(16, 8).Copy
    ThisWorkbook.Sheets("Report").Cells(4 + Cont, 9).PasteSpecial Paste:=xlPasteValues
    ThisWorkbook.Sheets("Historical").Cells(19, 8).Copy
    ThisWorkbook.Sheets("Report").Cells(4 + Cont, 13).PasteSpecial Paste:=xlPasteValues
  Next

End Sub

这个问题经常出现(事实上我自己也问过),所以这里是我在代码中使用的解决方案,以防它对其他人有用。问题在于BDH()电子表格函数是异步的,即它立即返回,在后台获取数据,然后在完成时告诉Excel。由于Excel只有一个执行线程,因此它仅在完成VBA子程序的处理后对该回调作出反应

递归调用VBA函数(参见类似问题的其他答案)以等待BDH数据返回的另一种方法是编写同步VBA电子表格函数。根据您对VBA的信心程度,您可能希望尝试这种方法。我还有等效的BDP/BDS函数调用,但在这里省略了它

此函数直接调用彭博数据API,在请求完成之前不会返回。它不提供BDH调用的所有选项(但如果需要,可以将这些选项添加到代码中),只返回每日数据,以最新可用数据结尾。另外一个优点是,数据以数组的形式返回,因此可以用作其他Excel函数的数组参数

用法: =getBloomiHistory(股票代码、字段、起始日期、包含日期)

参数:

  • 股票代码:任何Bbg股票代码(如“EUSA2 Curncy”)

    字段:任何Bbg历史字段或字段数组(例如“PX_MID”)

    startDate:作为Excel日期的特定开始日期(例如 “2021年5月21日”),或字符串(例如“20210521”或“-1CY”)

    includeDates:默认为True。如果为False,则返回序列不存在 具有日期,并且是值的单个向量。由于这是一个数组函数,因此返回的数据可以直接用作其他函数的参数

返回:包含从开始日期到今天的历史日期的变量

例如:

NB。为了运行代码,需要在Tools/References中勾选对Bloomberg COM API的引用。我更喜欢使用引用进行早期绑定,但您可以使用VBA
CreateObject(“blpapicom2.Session”)
来代替New()调用,并将所有对象定义为Object,但随后还需要使用VBA IDE中的对象浏览器(点击F2)查找所有BlpApi常量定义

VBA代码:

Option Explicit

Dim g_Session As blpapicomLib2.Session
Dim g_Service As blpapicomLib2.Service

'Function to set up the Bloomberg session once
Private Function CheckSession() As Boolean
    If g_Session Is Nothing Then
        Set g_Session = New blpapicomLib2.Session
        g_Session.Start
        g_Session.OpenService "//blp/refdata"
        Set g_Service = g_Session.GetService("//blp/refdata")
    End If
    CheckSession = True
End Function

Public Function getBloomiHistory(strSecurity As String, rngFields As Variant, vStart As Variant, Optional bIncludeDates As Boolean = True) As Variant
    Dim vRet As Variant
    
    CheckSession
    
    Dim req As blpapicomLib2.REQUEST
    Set req = g_Service.CreateRequest("HistoricalDataRequest")
    req.GetElement("securities").AppendValue strSecurity
    
    Dim vFields As Variant
    vFields = rngFields
    Dim strFields() As String
    Dim lFields As Long
    
    If IsArray(vFields) Then
        Dim lr, lc, lRows, lCols As Long
        lRows = UBound(vFields, 1)
        lCols = UBound(vFields, 2)
        lFields = lRows * lCols
        
        ReDim strFields(1 To lFields) As String
        Dim lstr As Long
        lstr = 1
        
        For lr = 1 To lRows
            For lc = 1 To lCols
                strFields(lstr) = vFields(lr, lc)
                req.GetElement("fields").AppendValue vFields(lr, lc)
                lstr = lstr + 1
            Next lc
        Next lr
    Else
        ReDim strFields(1) As String
        lFields = 1
        strFields(1) = vFields
        req.GetElement("fields").AppendValue vFields
    End If
    
    req.Set "nonTradingDayFillOption", "NON_TRADING_WEEKDAYS"
    req.Set "periodicityAdjustment", "ACTUAL"
    req.Set "periodicitySelection", "DAILY"
    
    Dim strStart As String
    If IsDate(vStart) Then
        strStart = Format(vStart, "yyyymmdd")
    Else
        strStart = vStart
    End If
     
    'endDate defaults to Today
    
    req.Set "startDate", strStart

    g_Session.SendRequest req
        
    Dim evt As blpapicomLib2.Event
    Dim msgIt As blpapicomLib2.MessageIterator
    Dim msg As blpapicomLib2.Message
    Dim eltRef As blpapicomLib2.Element
    Dim eltSecurities As blpapicomLib2.Element
    Dim eltSecurity As blpapicomLib2.Element
    Dim eltData As blpapicomLib2.Element
    Dim eltFieldDataArray As blpapicomLib2.Element
    Dim eltDate As blpapicomLib2.Element
    Dim eltValue As blpapicomLib2.Element
    Dim dt As Date
    Dim vt As Variant
    
    Dim bExit As Boolean
    bExit = False
    
    Do While Not bExit
        Set evt = g_Session.NextEvent
        If (evt.EventType = PARTIAL_RESPONSE Or evt.EventType = RESPONSE) Then
            Set msgIt = evt.CreateMessageIterator

            Do While (msgIt.Next)
                Set msg = msgIt.Message
                Set eltSecurities = msg.GetElement("securityData")
                Set eltFieldDataArray = eltSecurities.GetElement("fieldData")
                
                Dim nItems As Integer
                nItems = eltFieldDataArray.NumValues

                If bIncludeDates Then
                    ReDim vRet(1 To nItems, 1 To lFields + 1) As Variant
                Else
                    ReDim vRet(1 To nItems, 1 To lFields) As Variant
                End If

                Dim nItem As Integer
                Dim nValues As Integer
                Dim d As Double
                For nItem = 0 To nItems - 1
                    Set eltData = eltFieldDataArray.GetValueAsElement(nItem)
                    nValues = eltData.NumElements
                                     
                    Dim lOffset As Long
                    lOffset = 0
                    If bIncludeDates Then
                        Set eltDate = eltData.GetElement(0)
                        dt = eltDate.Value
                        vRet(nItem + 1, 1) = dt
                        lOffset = 1
                    End If
                    
                    Dim lField As Long
                    For lField = 1 To lFields
                        If eltData.HasElement(strFields(lField)) Then
                            Set eltValue = eltData.GetElement(strFields(lField))
                            vt = eltValue.Value
                            vRet(nItem + 1, lField + lOffset) = vt
                        Else
                            vRet(nItem + 1, lField + lOffset) = CVErr(xlErrNA)
                        End If
                    Next lField
                Next nItem
            Loop
     
            If (evt.EventType = RESPONSE) Then bExit = True
        End If
    Loop
    
    getBloomiHistory = vRet
End Function

我说“已经使用”,因为我现在使用编译的C++ Excel。XLL,它基本上做同样的事情,但有点快。

< P>这个问题经常出现(实际上我已经问过了),所以这里是我用代码的解决方案,以防它对任何人有用。问题在于BDH()电子表格函数是异步的,即它立即返回,在后台获取数据,然后在完成时告诉Excel。由于Excel只有一个执行线程,因此它仅在完成VBA子程序的处理后对该回调作出反应

递归调用VBA函数(参见类似问题的其他答案)以等待BDH数据返回的另一种方法是编写同步VBA电子表格函数。根据您对VBA的信心程度,您可能希望尝试这种方法。我还有等效的BDP/BDS函数调用,但在这里省略了它

此函数直接调用彭博数据API,在请求完成之前不会返回。它不提供BDH调用的所有选项(但如果需要,可以将这些选项添加到代码中),只返回每日数据,以最新可用数据结尾。另外一个优点是,数据以数组的形式返回,因此可以用作其他Excel函数的数组参数

用法: =getBloomiHistory(股票代码、字段、起始日期、包含日期)

参数:

  • 股票代码:任何Bbg股票代码(如“EUSA2 Curncy”)

    字段:任何Bbg历史字段或字段数组(例如“PX_MID”)

    startDate:作为Excel日期的特定开始日期(例如 “2021年5月21日”),或字符串(例如“20210521”或“-1CY”)

    includeDates:默认为True。如果为False,则返回序列不存在 具有日期,并且是值的单个向量。由于这是一个数组函数,因此返回的数据可以直接用作其他函数的参数

返回:包含从开始日期到今天的历史日期的变量

例如:

NB。为了运行代码,需要在Tools/References中勾选对Bloomberg COM API的引用。我更喜欢使用引用进行早期绑定,但您可以使用VBA
CreateObject(“blpapicom2.Session”)
来代替New()调用,并将所有对象定义为Object,但随后还需要使用VBA IDE中的对象浏览器(点击F2)查找所有BlpApi常量定义

VBA代码:

Option Explicit

Dim g_Session As blpapicomLib2.Session
Dim g_Service As blpapicomLib2.Service

'Function to set up the Bloomberg session once
Private Function CheckSession() As Boolean
    If g_Session Is Nothing Then
        Set g_Session = New blpapicomLib2.Session
        g_Session.Start
        g_Session.OpenService "//blp/refdata"
        Set g_Service = g_Session.GetService("//blp/refdata")
    End If
    CheckSession = True
End Function

Public Function getBloomiHistory(strSecurity As String, rngFields As Variant, vStart As Variant, Optional bIncludeDates As Boolean = True) As Variant
    Dim vRet As Variant
    
    CheckSession
    
    Dim req As blpapicomLib2.REQUEST
    Set req = g_Service.CreateRequest("HistoricalDataRequest")
    req.GetElement("securities").AppendValue strSecurity
    
    Dim vFields As Variant
    vFields = rngFields
    Dim strFields() As String
    Dim lFields As Long
    
    If IsArray(vFields) Then
        Dim lr, lc, lRows, lCols As Long
        lRows = UBound(vFields, 1)
        lCols = UBound(vFields, 2)
        lFields = lRows * lCols
        
        ReDim strFields(1 To lFields) As String
        Dim lstr As Long
        lstr = 1
        
        For lr = 1 To lRows
            For lc = 1 To lCols
                strFields(lstr) = vFields(lr, lc)
                req.GetElement("fields").AppendValue vFields(lr, lc)
                lstr = lstr + 1
            Next lc
        Next lr
    Else
        ReDim strFields(1) As String
        lFields = 1
        strFields(1) = vFields
        req.GetElement("fields").AppendValue vFields
    End If
    
    req.Set "nonTradingDayFillOption", "NON_TRADING_WEEKDAYS"
    req.Set "periodicityAdjustment", "ACTUAL"
    req.Set "periodicitySelection", "DAILY"
    
    Dim strStart As String
    If IsDate(vStart) Then
        strStart = Format(vStart, "yyyymmdd")
    Else
        strStart = vStart
    End If
     
    'endDate defaults to Today
    
    req.Set "startDate", strStart

    g_Session.SendRequest req
        
    Dim evt As blpapicomLib2.Event
    Dim msgIt As blpapicomLib2.MessageIterator
    Dim msg As blpapicomLib2.Message
    Dim eltRef As blpapicomLib2.Element
    Dim eltSecurities As blpapicomLib2.Element
    Dim eltSecurity As blpapicomLib2.Element
    Dim eltData As blpapicomLib2.Element
    Dim eltFieldDataArray As blpapicomLib2.Element
    Dim eltDate As blpapicomLib2.Element
    Dim eltValue As blpapicomLib2.Element
    Dim dt As Date
    Dim vt As Variant
    
    Dim bExit As Boolean
    bExit = False
    
    Do While Not bExit
        Set evt = g_Session.NextEvent
        If (evt.EventType = PARTIAL_RESPONSE Or evt.EventType = RESPONSE) Then
            Set msgIt = evt.CreateMessageIterator

            Do While (msgIt.Next)
                Set msg = msgIt.Message
                Set eltSecurities = msg.GetElement("securityData")
                Set eltFieldDataArray = eltSecurities.GetElement("fieldData")
                
                Dim nItems As Integer
                nItems = eltFieldDataArray.NumValues

                If bIncludeDates Then
                    ReDim vRet(1 To nItems, 1 To lFields + 1) As Variant
                Else
                    ReDim vRet(1 To nItems, 1 To lFields) As Variant
                End If

                Dim nItem As Integer
                Dim nValues As Integer
                Dim d As Double
                For nItem = 0 To nItems - 1
                    Set eltData = eltFieldDataArray.GetValueAsElement(nItem)
                    nValues = eltData.NumElements
                                     
                    Dim lOffset As Long
                    lOffset = 0
                    If bIncludeDates Then
                        Set eltDate = eltData.GetElement(0)
                        dt = eltDate.Value
                        vRet(nItem + 1, 1) = dt
                        lOffset = 1
                    End If
                    
                    Dim lField As Long
                    For lField = 1 To lFields
                        If eltData.HasElement(strFields(lField)) Then
                            Set eltValue = eltData.GetElement(strFields(lField))
                            vt = eltValue.Value
                            vRet(nItem + 1, lField + lOffset) = vt
                        Else
                            vRet(nItem + 1, lField + lOffset) = CVErr(xlErrNA)
                        End If
                    Next lField
                Next nItem
            Loop
     
            If (evt.EventType = RESPONSE) Then bExit = True
        End If
    Loop
    
    getBloomiHistory = vRet
End Function

我说“使用”,因为我现在使用编译的C++ Excel。XLL,它基本上做同样的事情,但速度稍快。

类似的问题:答案DS@伦敦提供的最好的方法是:转换代码使用BLAPPICOLIb2。一开始可能看起来很吓人,但如果你打算做这类事情,花时间是值得的。更同步的方式是