Данные Bloomberg не заполняются до завершения макроса Excel VBA - PullRequest
9 голосов
/ 29 декабря 2011

Я запускаю макрос в пустой книге 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

Ответы [ 4 ]

6 голосов
/ 28 августа 2013

Чтобы обойти эту проблему, нужно поместить все подпрограммы и т. Д., Которые вы хотите запустить после загрузки данных Bloomberg, в другую подпрограмму. Вы должны делать это каждый раз, когда звоните в Bloomberg. Если после Application.OnTime Now + TimeValue («00:00:15») вы вызываете другой сабвуфер в «master», он завершится неудачей - вы должны поместить все последующие сабы в новый master sub.

Например: Вместо

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

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

4 голосов
/ 30 декабря 2011

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

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

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

В Mr Excel цепочка сообщений: MrExcel Messageboard> Форумы вопросов> Вопросы Excel> Bloomberg ссылки и макросы.

Похоже, ключевая информация:

На вашем терминале Bloomberg, если вы введете WAPI , вы найдете списки 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, возможно, вы сможете обновить этот ответ для будущих посетителей.

1 голос
/ 23 декабря 2015

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

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

Приветствия

0 голосов
/ 12 июня 2018

Здравствуйте, я думаю, что нашел решение этой проблемы, и я действительно хочу поделиться этим с вами, ребята.

Прежде чем начать с реального ответа Я хочу убедиться, что все понимают, как на самом деле работает 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. x = 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, как предложил Тони Даллимор.

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

...