Данные Bloomberg не заполняются до тех пор, пока макрос Excel VBA не завершится

Я запускаю макрос в пустой книге Excel 2007 на ПК с лицензией Bloomberg. Макрос вставляет функции Bloomberg в sheet1, которые извлекают данные кривой доходности. Результаты некоторых дополнительных функций зависят от завершения первых функций и правильного отображения данных Бберга. Когда я прохожу программу, она отображает только "# N/A Requesting Data"., ". вместо результатов запроса, независимо от того, насколько медленно я иду. Поскольку некоторые функции зависят от заполнения строковых и числовых полей, программа обнаруживает ошибку во время выполнения в этом коде. Когда я прекращаю отладку - полностью заканчиваю запуск программы - появляются все значения Бберга, которые должны были заполниться. Я хочу, чтобы эти значения отображались во время работы программы.

Я пытался использовать комбинацию 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

Ответ 1

Способ обойти эту проблему состоит в том, чтобы поместить все субтитры и т.д., которые вы хотите запустить, после того, как вы поместили данные bloomberg в другое подразделение. Вы должны делать это каждый раз, когда вы звоните в информацию Bloomberg. Если вы вызовете другой sub в "master" sub после 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

Надеюсь, что поможет

Ответ 2

Я погуглил BloombergUI.RefreshAllStaticData и сразу попал на эту страницу Mr Excel: http://www.mrexcel.com/forum/showthread.php?t=414626

Мы не должны публиковать ответы, которые являются только ссылками в случае, если эта ссылка исчезнет и заберет ответ. Однако я не уверен, что понимаю вопрос или ответ достаточно хорошо, чтобы суммировать его.

Ссылка Google, вероятно, будет существовать в обозримом будущем.

В рамках г-на Excel цепочка состоит из: доски объявлений MrExcel> Форумы вопросов> Вопросы Excel> Ссылки и макросы Bloomberg.

Ключевая информация выглядит так:

На вашем терминале Bloomberg, если вы введете WAPI & lt; GO> вы найдете списки API Bloomberg и загружаемые примеры.

Используя информацию справочного файла в этой области, мы можем создать более надежное решение для этого с помощью библиотеки типов данных 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

После того, как вы опробовали решение Mr Excel, возможно, вы сможете обновить этот ответ для будущих посетителей.

Ответ 3

Я собрал некоторую информацию из Интернета и написал, что 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

Это должно быть произвольная задача, вызвав sub подобно 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

Любые комментарии приветствуются. Возможное улучшение может заключаться в том, чтобы рабочая тетрадь и/или рабочий лист были входом функции, но я действительно не видел необходимости в этом.

Приветствия

Ответ 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. х = 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. х = 3
  2. Application.OnTime Now + TimeSerial (0, 0, 2), "Sub2"
  3. Здесь оставшийся код в Sub First() выполняется до КОНЦА, прежде чем переключиться на Sub Second().
  4. Таким образом, x = 2 * x выполняется сразу же вместе с каждой строкой кода, которая появляется до конца Sub First(). Теперь х равен 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().

ПОЧЕМУ ЭТО ВАЖНО?

Потому что в свете того, что я только что объяснил, теперь я могу показать вам свое решение вопроса ОП. Тогда вы можете адаптировать его к вашим потребностям.

И да!!! Это также работает с For Loops!

РЕШЕНИЕ

У меня есть две подпрограммы:

  1. BLPDownload(), где я обновляю рабочую книгу, и мне нужно дождаться загрузки значений, чтобы выполнить какой-то другой код...

  2. BLPCheckForRefresh(), где я проверяю, все ли данные были загружены

Так же, как и раньше, я объявляю две переменные с областью действия уровня модуля

Dim firstRefreshDone As Boolean, Refreshing As Boolean

Sub BLPDownload()

CHECK:

Что я делаю ниже:

  • проверьте, не сказал ли я уже VBA "Обновить книгу". Конечно, при первом запуске макроса у вас нет; следовательно firstRefreshDone = False и он входит в этот блок оператора if.
  • Затем он вызывает другую Sub BLPCheckForRefresh() и выходит из текущей подпрограммы.

И это хитрость. Для выхода из подпрограммы после вызова Application.OnTime *

Внутри BLPCheckForRefresh() происходит

  • что я установил значение firstRefreshDone = True
  • проверьте, есть ли в UsedRange ячейки С# N/A запрашивающими данными. Если у меня есть, значение Refreshing = 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, запрашивающие данные, он вызывает другую Sub BLPCheckForRefresh() и снова выходит из текущей подпрограммы.

Эта забавная игра продолжается до тех пор, пока у нас не останется # N/A запрашивающих данных в нашем UsedRange

    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

И это цикл, в котором я прохожу каждую ячейку в используемом диапазоне в поисках # N/A Запроса данных

    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, и другой переменной Scope на уровне модуля, которая хранит счетчик количества итераций , эту структуру можно использовать и в For Loops.

При этом я хочу отметить, что, по моему мнению, лучшее решение этой проблемы - это использование API Bloomberg, как это предложил Тони Даллимор.

Надеюсь это поможет!!