Данные 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
Я думал, что команды были выполнены в следующем порядке:
- х = 3
- Application.OnTime Now + TimeSerial (0, 0, 2), "Sub2"
- Затем через 2 секунды ожидания в Sub Second() x = x + 1, следовательно, 4
- Наконец, мы возвращаемся к Sub First(), где x = 2 * x, так что в конце x равен 8.
Оказывается, это не тот способ, которым работает VBA; вместо этого происходит следующее:
- х = 3
- Application.OnTime Now + TimeSerial (0, 0, 2), "Sub2"
- Здесь оставшийся код в Sub First() выполняется до КОНЦА, прежде чем переключиться на Sub Second().
- Таким образом, x = 2 * x выполняется сразу же вместе с каждой строкой кода, которая появляется до конца Sub First(). Теперь х равен 6.
- Наконец, после 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!
РЕШЕНИЕ
У меня есть две подпрограммы:
BLPDownload(), где я обновляю рабочую книгу, и мне нужно дождаться загрузки значений, чтобы выполнить какой-то другой код...
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, как это предложил Тони Даллимор.
Надеюсь это поможет!!