Wait until Excel finishes populating Bloomberg data using VBA - excel

I have an Excel sheet with ~300,000 BDH formulas to download securities prices.
I want to
open the file and get all prices
paste them as values
save and close the file.
However, I do not know when Excel finishes populating Bloomberg data, so it's difficult to determine the time to do 2) and 3).
I have written VBA, but not sure if it works:
In Module1
Sub CheckFormulas()
If Application.CalculationState = xlDone Then
With Worksheets("Sheet1").UsedRange
.Value = .Value
End With
Else
Application.OnTime Now + TimeValue("00:30:00"), "CheckFormulas"
ActiveWorkbook.Close Savechanges:=True
End If
End Sub
In 'ThisWorkbook'
Private Sub Run()
Call CheckFormulas
End Sub
So the way it should theoretically work is:
I open the file
the code checks every 30 minutes if all BDH formulas are done
if they are done, then paste as values, save and close 4) if not, check 30 minutes later again.
I am wondering if this is the best way to accomplish my goal and want to know if Application.CalculationState is compatible or works with Bloomberg formulas BDH?

In our production environment we use a cell that checks if there are cells with "Req" in their formula:
=SUM(IFERROR(FIND("Req", _range_to_check_with_formulas_ ),0))
In a VBA Sub, we test the cell value with a WHILE LOOP:
while cell_to_test.value <> 0
Application.Calculate
Application.RTD.RefreshData
DoEvents
Wend
Call _sub_to_do_some_stuff_

Built on #stexcec's answer, since I do not want to add a test cell in the worksheet, I use VBA to search for "requesting" based on values. The following function is used to find "requesting" in the Range containing Bloomberg formulae:
Function IsFinished(ws As Worksheet) As Boolean
Dim i, j, LastRow, LastCol As Integer
Dim r, c As Range
LastRow = ws.Range("A1").End(xlDown).Row
LastCol = ws.Range("A1").End(xlToRight).Column
Set r = ws.Range(ws.Cells(1, 1), ws.Cells(LastRow, LastCol))
Set c = r.Find("Requesting", LookIn:=xlValues)
If c Is Nothing Then
IsFinished = True
Else
IsFinished = False
End If
End Function
Then, similar to #stexcec's answer, the following code to used to wait until all cells are refreshed:
' Wait until updated
Do While True
Application.Calculate
Application.RTD.RefreshData
DoEvents
If IsFinished(Application.ActiveSheet) Then
Exit Do
End If
Loop

Related

loop vba code to call macro through range

I have a macro which looks at a selection of data in specified cells, it takes the value from each row in the selection and populates an excel spreadsheet. The it calls another macro which runs.
This code works well when I start the start and finish cells, however I want to be able to use a variable range so it automatically finds the last cell and runs through the selection.
I've tried using 'For Each rngMyCell In Sheets("Selection").Range("A43").End(xlDown).Row however this only selects the last item in my list and doesn't run through all of them. Can anyone advise what I'm doing wrong.
Thanks,
Sub Macro1()
Sheets("Selection").Range("B6").Value = Now()
Dim rngMyCell As Range
Application.ScreenUpdating = False
For Each rngMyCell In Sheets("Selection").Range("A43:A50")
Sheets("Selection").Range("C3").Value = rngMyCell
Call RunAll
Next rngMyCell
Application.ScreenUpdating = True
Sheets("Selection").Range("B7").Value = Now()
MsgBox ("All done")
End Sub
In posted code: For Each rngMyCell In Sheets("Selection").Range("A43").End(xlDown).Row
you are looping through a number, but not range (which is wrong). The group in For Each...Next statement should be name of an object collection or array.
To loop dynamically through whole range, try this:
For Each rngMyCell In Sheets("Selection").Range("A43:A" & Sheets("Selection").Range("A43").End(xlDown).Row)

Build Excel function: Unmerge, calculate, re-merge. Problem: Function starts to run recursive before finishing

My main goal is to be able to autofilter merged cells in one column.In the picture below I want row 7-9 to disappear when I remove "6" from the autofilter menu. But as I have figured, I need the value "6" to be held in all the cells "L7:L9" in order for Excel to do so.
The number 6 is calculated by adding "Num1" and "Num2" (2 * 3) by the following function I have placed in "L7":
Function Exposure(arg1 As Range, arg2 As Range) As Variant
Application.EnableEvents = False
Application.Calculation = xlManual
If Application.ThisCell.Offset(, -1).Value <> "-" And Application.ThisCell.Offset(, -2).Value <> "-" Then
Exposure = Left(Application.ThisCell.Offset(, -1).Value, 1) * Left(Application.ThisCell.Offset(, -2).Value, 1)
End If
If Exposure = 0 Then Exposure = "-"
Application.Calculation = xlAutomatic
Application.EnableEvents = True
End Function
I put the following formula inside the merged cell "L7":=Exposure(K7;J7). Then formula is dragged down."Num1" and "Num2" are controlled by valdiation fields, drop-down menu.
My plan was to unmerge after calculating the Exposure Variant, fill the same value in the remaining rows, then re-merge the same area. So I wrote this stand alone Sub:
Sub WorkingSub(rng As Range)
'Set rng = ActiveCell.MergeArea
rng.UnMerge
For i = 2 To rng.Cells.Count
rng.Cells(i).Value = rng.Cells(1).Value 'This line triggers recursion
Next i
rng.Offset(rng.Cells.Count).Copy 'Copies format from below
rng.PasteSpecial Paste:=xlPasteFormats 'Paste that keeps the values even after merging
End Sub
Which works on its own, but not when called inside the function above. After setting the first value, the function triggers "something", debug show the the function starting over, skipping the rng.PasteSpecial Paste:=xlPasteFormats code.
So my question to you guys is how do i write my function(s) to stop "recursing" and let me unmerge during the function call?
Or am I attacking this the wrong way? What would you do?
I am stuck with merged cells for lots of reasons, this is just one part of many inside this spreadsheet.
An interesting problem. You can capture the filter event through trapping a change in a calculation and then processing the rows of the table for visibility. I've made some assumptions for the initial table range assignment which may need some alteration.
The If Not VisRange Is Nothing Then is actually redundant as the prior line will throw a fit if an empty range is assigned, but I just kept it in. In order to get around having a null range, keep the header range in the initial MergedTableRange so there will always be a row visible
Within a cell either somewhere in the same worksheet or a 'dummy' worksheet
=SUBTOTAL(103,Sheet1!A3:H10) 'Or other table range
In the worksheet module code
Private Sub Worksheet_Calculate()
Dim ws As Worksheet: Set ws = Worksheets("Sheet1")
Dim MergedTableRange As Range: Set MergedTableRange = ws.Range("A2").CurrentRegion
Dim Cell As Range
Dim VisRange As Range: Set VisRange = MergedTableRange.SpecialCells(xlCellTypeVisible)
If Not VisRange Is Nothing Then
For Each Cell In VisRange
If Not Application.Intersect(Cell.MergeArea, VisRange).Address = Cell.MergeArea.Address Then
Cell.Rows.Hidden = True
End If
Next Cell
End If
End Sub
I came up with a different approach. Maybe there's a downside I'm missing. But my few test runs have succeeded.
I allready have a hidden sheet named "Template" where the formats for each new "#" is stored. So whenever the user wants to insert a new row, the template have the merged and the non-merged cells ready and insert is done through copy paste.
In that same sheet I made 2 merged rows in column 2, 3 merged cells in column 3 and so on:
This way I'm able to copy the correct number of merged rows to paste after filling the unmerged rows with their correct values.
I came to the conclusion that I could catch a Worksheet_change on the "Num1" and "Num2" columns instead of catching and canceling an autofilter call.
So I added:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Target.Worksheet.Range("J:J")) Is Nothing Then
Call UnMergeMerge(Cells(Target.Row, "L").MergeArea)
End If
If Not Intersect(Target, Target.Worksheet.Range("K:K")) Is Nothing Then
Call UnMergeMerge(Cells(Target.Row, "L").MergeArea)
End If
End Sub
And the UnMergeMerge sub ended up being:
Sub UnMergeMerge(rng As Range)
Application.EnableEvents = False
Application.ScreenUpdating = False
rng.UnMerge
For i = 2 To rng.Cells.Count
rng.Cells(i).Value = rng.Cells(1).Value
Next i
With Sheets("Template")
.Range(.Cells(8, rng.Cells.Count), .Cells(8 + rng.Cells.Count, rng.Cells.Count)).Copy
End With
rng.PasteSpecial Paste:=xlPasteFormats
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Still not sure it's the fastest and best approach...Do you guys still believe catching, undoing and running a different autofilter would be more effective?

Is there a VBA code to select multiple (and varied totals of) columns from multiple worksheets to copy and paste into a new worksheet

I am trying to copy multiple columns from multiple worksheets into a new worksheet in Excel using a VBA Macro.
I have already created the worksheet, and I want to paste specific columns one after another in that worksheet.
I would like to copy from each worksheet all columns including and beyond a certain column, in all worksheets including and from Column F.
I have written a piece of code that selects the appropriate data and loops correctly.
However, i get a "run-time error 1004", when the loop hits a worksheet where I am copying only one column.
I know this is because of the choice of my code. However, I don't know how to solve the problem.
The problem is that my code selects a range to the end of the worksheet when there is only one column being selected. This creates a copied area too big to paste in the new worksheet.
Dim i As Integer
i = 1
Do While i <= Worksheets.Count - 1
Worksheets(i).Select
'Select, Copy and Paste Data
RangeFromF1
Selection.Copy
Worksheets("Combined").Select
Range("X1").Select
Selection.End(xlToLeft).Select
ActiveCell.Offset(0, 1).Select
ActiveSheet.Paste
i = i + 1
Loop
End Sub
Public Sub RangeFromF1()
Range("F1", Range("F1").End(xlDown).End(xlToRight)).Select
End Sub
Instead of going from column F to the right, try going from the last column to the left.
Public Sub RangeFromF1()
Range("F1", Cells(1, Columns.Count).End(xlToLeft).End(xlDown)).Select
End Sub
You might also want to get rid of all the Select stuff.
Sub CopyStuff()
Dim i As Long
i = 1
Do While i <= Worksheets.Count - 1
With Worksheets(i)
.Range("F1", .Cells(1, .Columns.Count).End(xlToLeft).End(xlDown)).Copy
Worksheets("Combined").Cells(1, Columns.Count).End(xlToLeft).Offset(, 1).Paste
i = i + 1
End With
Loop
End Sub
Before coming back to check for your answer noris, I figured out a way, to do as you suggested, with the following code:
Public Sub ReferenceSelection()
Dim startcell As Range
Set startcell = Range("A1").End(xlDown).End(xlToRight)
Range(startcell, ("F1")).Select
End Sub

How can I do a Calculation in Microsoft Excel VBA?

I'm 15 and I'm doing a Internship as a Developer and I've got a kinda hard exercise.
I have a Table with 3 columns, A is "Number" B is "percent" and C is "Value". The column "value" is blank and I Need to calculate the value with a macro button. I've tried this, but it was wrong because I didnĀ“t calculate it in VBA:
Public Sub PushButton ()
Range("C2:C11").Formula = "=A2*B2/100"
Range("C2:C11").Value = Range("C1:C6).Value
End Sub
How do I solve this?
You are using a defined range, you could do it with a dynamic range like this:
Option Explicit
Sub PushButton()
Dim i As Long, LastRow As Long
With ThisWorkbook.ActiveSheet
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row 'first you need to find the last row on the active sheet
For i = 2 To LastRow 'then iterate through all the rows starting from 2, if row 1 has headers
.Cells(i, 3) = .Cells(i, 1) * .Cells(i, 2) / 100
Next i
End With
End Sub
If you need help understanding this code, let me know.
Edit: Explanation
Well, the first thing you must do is Dimension all your variables, and to help that you can use the Option Explicitright above all your code.
I've dimensioned 1 variable for the loop and another one to find the last row with text.
To find the last row what you are actually doing is going to excel, select the last row (1048576) and the column where it will have text, in this case 1 or column "A" and then pushing ctrl+Up excel and vba will get you to the last cell with text.
To do that you use Cells(Row, column) instead of manually inserting row 1048576 you can just use rows.count and it will be the same.
Once you get the last row you just iterate with a For iloop meaning For a variable called i which equals 2 (For i = 2) To LastRow (to the last row you calculated) VBA will repeat the code in between the ForAnd Next adding 1 number to i everytime the loop restarts.
In this case is just adding a number to the rows on Cells(i, 3) so you can modify that cell depending of its i value.
I think you need to question why Excel needs to calculate on demand rather than automatically like normal. Failing that there are a few options
You could change your calculation method to Manual using the following in the ThisWorkbook object
Option Explicit
Dim xlCalcMethod As XlCalculation
Private Sub Workbook_Open()
With Application
' Store users current method for when closing the workbook
xlCalcMethod = .Calculation
.Calculation = xlCalculationManual
End With
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
' Reset calculation
Application.Calculation = xlCalcMethod
End Sub
and then when the button is pressed use the following code to calculate placed in a Module
Option Explicit
Public Sub Button_Click()
Application.Calculate
End Sub
Another option to do this without looping would be:
Sub CalculateRange()
Dim rng As Range
' Update for your Range
With ActiveSheet
Set rng = .Range("C2:C" & .Cells(.Rows.Count, "A").End(xlUp).Row)
End With
rng.Value2 = Evaluate(rng.Offset(0, -2).Address & "*" & rng.Offset(0, -1).Address & "/100")
End Sub
Finally, the way you've come up with is perfectly acceptable as VBA

For Loop Copy and Paste Excel VBA

Function Iterate()
Dim i As Integer
For i = 1 To 10
Worksheets("Calculator").Calculate
Worksheets("Calculator").Range("AC6:AC16").Copy Destination:=Sheets("Iterations").Range("A1:A10")
Worksheets("Calculator").Range("AT10:AT11").Copy Destination:=Sheets("Iterationas").Range("A11:A12")
Worksheets("Iterations").Paste
Next
End Function
My Goal, is to run this loop as many times as I'll need, and after every single loop, I want excel to take the Range("cells") and copy them to the "Iterations" worksheet.
The data on Calculator is refreshed every loop, so new calculations appear. Once the new calculations appear, I want to paste it one next to the other (which I don't know how to do).
For now, this gives me a runtime error Subscript it out of range
Any advice?
I have tried the following:
Option Explicit
Public Sub Iterate()
Dim i As Long
For i = 1 To 10
Worksheets(1).Calculate
Worksheets(1).Range("AC6:AC16").Copy Destination:=Worksheets(2).Range("A1:A10")
Worksheets(1).Range("AT10:AT11").Copy Destination:=Worksheets(2).Range("A11:A12")
Next
End Sub
It works, just make sure that you rename the Worksheets(1) and (2) relevantly. In general, use Function, when you expect a value to be returned. For changes in a worksheet, use Sub.
In general, what you wanted in the comments is to copy the values and to put them next to each other column. Here is how to get it:
Option Explicit
Public Sub Iterate()
Dim i As Long
For i = 1 To 10
With Worksheets(1)
Worksheets(2).Calculate
Worksheets(2).Range("AC6:AC16").Copy
.Cells(1, i).PasteSpecial Paste:=xlPasteValues
Worksheets(2).Range("AT10:AT11").Copy
.Cells(1, i + 10).PasteSpecial Paste:=xlPasteValues
Application.CutCopyMode = False
End With
Next
End Sub

Resources