【问题标题】:Bloomberg data doesn't populate until Excel VBA macro finishes在 Excel VBA 宏完成之前,彭博数据不会填充
【发布时间】:2012-01-29 23:43:37
【问题描述】:

我正在使用 Bloomberg 许可证的 PC 上的空白 Excel 2007 工作簿中运行宏。该宏将 Bloomberg 函数插入到 sheet1 中以提取收益率曲线数据。一些附加函数的结果取决于完成并正确显示 Bberg 数据的第一个函数。当我单步执行程序时,它只显示 '#N/A Requesting Data 。 . 。而不是查询的结果,无论我走多慢。由于某些函数依赖于填充的字符串和数字字段结果,因此程序会在该代码处遇到运行时错误。当我停止调试时——完全结束运行程序——所有应该填充的 Bberg 值都会出现。我希望在程序仍在运行时显示这些值。

我尝试使用 DoEvents 和 Application.OnTime() 的组合将控制权返回给操作系统并让程序等待很长时间以等待数据更新,但都没有奏效。任何想法都会有所帮助。我的代码如下。 wb 是全局级别的工作簿,ws1 是全局级别的工作表。

Public Sub Run_Me()

    'Application.DisplayAlerts = False
    'Application.ScreenUpdating = False

    Call Populate_Me
    Call Format_Me

    'Application.DisplayAlerts = True
    'Application.ScreenUpdating = True

End Sub
Private Sub Populate_Me()

    Dim lRow_PM As Integer
    Dim xlCalc As XlCalculation

    Set wb = ThisWorkbook
    Set ws1 = wb.Sheets(1)

    'clear out any values from previous day
    If wb.Sheets(ws1.Name).Range("A1").Value <> "" Then
        wb.Sheets(ws1.Name).Select
        Selection.ClearContents
    End If


    xlCalc = Application.Calculation
    Application.Calculation = xlCalculationAutomatic

    Range("A1").Value = "F5"
    Range("B1").Value = "Term"
    Range("C1").Value = "PX LAST"

    Range("A2").Select
    ActiveCell.FormulaR1C1 = "=BDS(""YCCF0005 Index"",""CURVE_MEMBERS"",""cols=1;rows=15"")"
    BloombergUI.RefreshAllStaticData

    Range("B2").Select
    ActiveCell.FormulaR1C1 = "=BDS(""YCCF0005 Index"",""CURVE_TERMS"",""cols=1;rows=15"")"
    BloombergUI.RefreshAllStaticData

    Application.OnTime Now + TimeValue("00:00:10"), "HardCode"

    '******more code*******'
    End Sub
Sub HardCode()

    Range("C2").Select
    ActiveCell.FormulaR1C1 = "=BDP($A2,C$1)"
    BloombergUI.RefreshAllStaticData

End Sub

【问题讨论】:

  • Here 是您需要的答案!

标签: excel excel-2007 bloomberg vba


【解决方案1】:

解决此问题的一种方法是在将Bloomberg 数据拉入不同的子程序后,将要运行的所有子程序等放入。您每次致电彭博资讯时都必须这样做。如果您在 Application.OnTime Now +TimeValue("00:00:15") 之后调用“主”子中的另一个子,它将失败 - 您必须将所有子跟随到一个新的主子中。

例如: 而不是

Sub Master1()
Application.Run "RefreshAllStaticData"
Application.OnTime Now + TimeValue("00:00:15"), "OtherSub1"
'This will cause the Bloomberg Data to not refresh until OtherSub2 and 3 have run
OtherSub2
OtherSub3
End Sub

应该是

Sub Master1()
Application.Run "RefreshAllStaticData"
Application.OnTime Now + TimeValue("00:00:15"), "Master2"
End Sub

Sub Master2()
OtherSub1
OtherSub2
OtherSub3
End Sub

希望有帮助

【讨论】:

  • 当你有 for 循环时你会怎么做?
  • 当有 for 循环时,整个事情都不起作用。
【解决方案2】:

我在 Google 上搜索了 BloombergUI.RefreshAllStaticData,并立即被带到这个 Excel 先生页面:http://www.mrexcel.com/forum/showthread.php?t=414626

我们不应该发布只是链接的答案,以防链接消失并带走答案。但是,我不确定我是否理解问题或答案是否足以概括它。

Google 链接可能会在可预见的未来存在。

在 Excel 先生中,链是:MrExcel 留言板 > 问题论坛 > Excel 问题 > 彭博链接和宏。

关键信息似乎是:

在您的 Bloomberg 终端上,如果您输入 WAPI ,您将找到 Bloomberg API 的列表和可下载的示例。

使用该区域的帮助文件信息,我们可以使用 Bloomberg 数据类型库为此构建更强大的解决方案。转到工具 |引用并添加对此库的引用。然后可以使用此代码填充单元格:

Sub Test2()
    Dim vResults, vSecurities, vFields
    Dim objBloomberg As BLP_DATA_CTRLLib.BlpData

    'fill our arrays - must be 1 dimension so we transpose from the worksheet
    With Application.WorksheetFunction
        vSecurities = .Transpose(Sheet1.Range("B2:B4").Value)
        vFields = .Transpose(.Transpose(Range("C1:H1").Value))
    End With

    Set objBloomberg = New BLP_DATA_CTRLLib.BlpData
    objBloomberg.AutoRelease = False

    objBloomberg.Subscribe _
            Security:=vSecurities, _
            cookie:=1, _
            Fields:=vFields, _
            Results:=vResults

    Sheet1.Range("C2:H4").Value = vResults
End Sub

一旦您试用了 Excel 先生的解决方案,也许您可​​以更新此答案以造福未来的访问者。

【讨论】:

  • 我喜欢您推荐使用 WAPI,但我不认为它解决了原始问题,即为什么电子表格 BBG 公式在宏停止运行之前不计算。
【解决方案3】:

我从网络上收集了一些信息,并写了一个改进版本,与我迄今为止发现的所有内容相比:

Private WaitStartedAt As Double
Private Const TimeOut As String = "00:02:00"

Public Function BloomCalc(Callback As String) As Boolean
    Dim rngStillToReceive As Range
    Dim StillToReceive As Boolean
    Dim ws As Worksheet
    StillToReceive = False
    If WaitStartedAt = 0 Then
        WaitStartedAt = TimeValue(Now())
    End If
    If TimeValue(Now()) >= WaitStartedAt + TimeValue(TimeOut) Then
        GoTo errTimeOut
    End If
    For Each ws In ActiveWorkbook.Worksheets
        Set rngStillToReceive = ws.UsedRange.Find("*Requesting Data*", LookIn:=xlValues)
        StillToReceive = StillToReceive Or (Not rngStillToReceive Is Nothing)
    Next ws
    If StillToReceive Then
        BloomCalc = False
        Application.OnTime Now + (TimeSerial(0, 0, 1)), Callback
    Else
        WaitStartedAt = 0
        BloomCalc = True
    End If
    Exit Function
errTimeOut:
    Err.Raise -1, , "BloomCalc: Timed Out. Callback = " & Callback
End Function

它应该通过调用像 DoSomething() 这样的 sub 来执行任意任务

Public Sub DoSomething() 
    DoSomethingCallback
End Function

这会调用一个“回调”函数,该函数将调用自身,直到数据被刷新或达到时间限制

Public Sub AutoRunLcbCallback()
    If BloomCalc("AutoRunLcbCallback") Then
        MsgBox "Here I can do what I need with the refreshed data"
        ' for instance I can close and save the workbook
        ActiveWorkbook.Close True
    End If
End Sub

感谢任何评论。一个可能的改进可能是允许工作簿和/或工作表作为函数的输入,但我真的没有看到需要这样做。

干杯

【讨论】:

  • 我喜欢这个实现。但是你应该给 Application.OnTime 更多的延迟,也许是 5 秒而不是 1 秒。如果延迟太短,您最终可能会在 vba 中出现一些奇怪的应用程序错误。
【解决方案4】:

你好,我想我已经找到了解决这个问题的方法,我真的很想和你们分享这个。

在开始真正的答案之前我想确保每个人都了解 Application.OnTime 的实际工作原理。如果您已经知道,那么您可以安全地跳到下面的解决方案

让我们做一个 TOY EXAMLPE 示例,其中包含两个子例程 Sub First() 和 Sub Second() 以及一个在外部声明的变量 x,以便它在整个模块内具有范围

Dim x as integer
Sub First()
    x = 3
    Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2"
    x = 2*x
End Sub

Sub Second() 
    x = x + 1
End Sub

我认为命令是按以下顺序执行的:

  1. x = 3
  2. Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2"
  3. 然后在等待 2 秒后,在 Sub Second() 中 x = x + 1,因此为 4
  4. 最后我们回到 Sub First() 其中 x = 2*x ,所以最后 x 等于 8。

原来这不是VBA的运作方式;相反,发生的是:

  1. x = 3
  2. Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2"
  3. 这里 Sub First() 中的剩余代码执行到 THE END,然后再切换到 Sub Second()。
  4. 因此,x = 2*x 会立即与出现在 Sub First() 结束前的每一行代码一起执行。现在 x 等于 6。
  5. 最后,等待2秒后,执行Sub Second()中的指令,x = x + 1,最终x等于7

这与您让应用程序等待的时间无关。因此,例如,如果在我的示例中,在

之后
Application.OnTime Now + TimeSerial(0, 0, 2), "Sub2" 

VBA 需要 10 秒来执行该行

x = 2*x

它仍然必须在切换到 Sub Second() 之前完成该命令的执行。

为什么这很重要?

因为根据我刚才解释的内容,我现在可以向您展示我对 OP 问题的解决方案。然后,您可以根据自己的需要进行调整。

是的!这也适用于 For 循环!

解决方案

我有两个子程序:

  1. BLPDownload() 刷新工作簿的地方,我必须等待下载值才能执行其他代码...

  2. BLPCheckForRefresh() 检查是否所有数据都已下载

所以和之前一样,我用模块级作用域声明了两个变量

Dim firstRefreshDone As Boolean, Refreshing As Boolean

Sub BLPDownload()

CHECK:

我在下面做的是:

  • 检查我是否已经告诉 VBA 刷新工作簿。当然,你第一次运行宏时你没有;因此 firstRefreshDone = False 并进入 if 语句的这个块。
  • 接下来,它调用另一个 Sub BLPCheckForRefresh() 并退出当前的 Subroutine。

这就是诀窍。调用 Application.OnTime* 后退出子程序

在 BLPCheckForRefresh() 内部发生了什么

  • 我设置了 firstRefreshDone = True 的值
  • 检查在 UsedRange 中是否有带有 #N/A Requesting Data 的单元格。如果我有,刷新的值 = True。
  • 最后我回调了 Sub BLPDownload()

    If Not firstRefreshDone Then
        Application.Run "RefreshEntireWorkbook"
        Application.Run "RefreshAllStaticData"
        Application.OnTime Now + TimeSerial(0, 0, 4), "BLPCheckForRefresh"
        Exit Sub
    

不过这一次,firstRefreshDone = True 所以,如果刷新也完成了,它会转到 AFTER_REFRESH ,您可以在其中放置您想要的所有代码,否则 ...

    ElseIf Not Refreshing Then
        GoTo AFTER_REFRESH

如果刷新未完成,即如果我有带有 #N/A Requesting Data 的单元格,它会调用另一个 Sub BLPCheckForRefresh() 并再次退出当前子例程。

这个有趣的游戏一直在继续,直到我们的 UsedRange 中没有更多的 #N/A 请求数据

    Else
        Refreshing = False
        Application.OnTime Now + TimeSerial(0, 0, 4), "BLPCheckForRefresh"
        Exit Sub
    End If

AFTER:
    some code ...
End Sub

这是我检查刷新是否完成的子。

Sub BLPCheckForRefresh()
    Dim rng As Range, cl As Range
    Set rng = Foglio1.UsedRange

如上所述,我设置了 firstRefreshDone = True 的值

    firstRefreshDone = True

这是我遍历 usedrange 中的每个单元格以查找#N/A Requesting Data 的循环

    On Error Resume Next
    For Each cl In rng
        If InStr(cl.Value2, "#N/A Request") > 0 Then
            Refreshing = True
            Exit For
        End If
    Next cl
    On Error GoTo 0

最后我回调了 Sub BLPDownload()

    Call BLPDownload
End Sub

所以这是我的解决方案。我为我工作,并使用另一个肮脏的技巧,它总是利用 GoTo 语句和另一个模块级范围变量来计算迭代次数也可以在 For 循环中使用这种结构。

话虽如此,我想指出,在我看来,解决这个问题的最佳方法是按照 Tony Dallimore 的建议使用 Bloomberg API。

希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-08
    • 2013-04-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多