10

我在具有 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
4

4 回答 4

7

A way to get around this issue is to put all subs, etc that you want to run after pulling the bloomberg data into a different sub. You must do this each time you call Bloomberg information. If you call another sub in the "master" sub after the Application.OnTime Now +TimeValue("00:00:15"), it will fail- you must put all subs following into a new master sub.

For example: Instead of

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

It should be

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

Sub Master2()
OtherSub1
OtherSub2
OtherSub3
End Sub

Hope that helps

于 2013-08-28T17:34:21.827 回答
5

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

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

在可预见的未来,谷歌链接可能会存在。

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

关键信息似乎是:

在您的 Bloomberg 终端上,如果您输入 WAPI < GO >,您将找到 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 先生的解决方案,也许您可​​以更新此答案以造福未来的访问者。

于 2011-12-29T21:51:52.763 回答
2

我从网上收集了一些信息,并写下了与我目前发现的所有内容相比,imho 是一个改进的版本:

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() 这样的子任务来执行任意任务

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

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

干杯

于 2015-12-23T12:59:57.533 回答
1

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

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

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

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() 检查所有数据是否已下载

所以就像以前一样,我用 Module-Level Scope 声明了两个变量

Dim firstRefreshDone As Boolean, Refreshing As Boolean

Sub BLPDownload()

CHECK:

我在下面做的是:

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

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

在 BLPCheckForRefresh() 内部会发生什么

  • 我设置了 firstRefreshDone = True 的值
  • 检查在 UsedRange 中是否有带有 #N/A 请求数据的单元格。如果我有,刷新的值 = 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 Requesting Data

    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。

希望这可以帮助!!

于 2018-06-12T19:15:01.017 回答