0

I am in need to optimizing some VBA which currently works functionally.

Given columns of sequential Dates (column B) and Times (column C), and Given a time window (T1 and T2), return a range of rows in which dates and times fall within T1 and T2. For example, I want MIN and MAX price between those two times.

The goal is to build Open/High/Low/Close charts for Excel candlestick charts and the data source has over 260,000 rows of data.

I currently have the following code that

Dim priceRange As Range
startRowNum = GetFirstRow(StartTime)     << THIS TAKE 10 SECONDS
endRowNum = GetLastRow(endTime)         << THIS TAKE 10 SECONDS
Set priceRange = Range(Cells(startRowNum, 4), Cells(endRowNum, 4))
targetRange.Offset(0, 2).Value = Application.WorksheetFunction.Max(priceRange) 
targetRange.Offset(0, 3).Value = Application.WorksheetFunction.Min(priceRange) 

To find the first row...

Function GetFirstRow(T As Date) As Long

'Starts at FirstRow and returns the first row where the time is greater than T1.

Dim currentRow As Long
Dim CompareTime As Date
Dim CompareDate As Date

currentRow = 4 'Start at row4 due to headers.

Do While (IsDate(Cells(currentRow, 2)))
    CompareDate = Cells(currentRow, 2)
    CompareTime = Cells(currentRow, 3)
    marketTime = CompareDate + CompareTime
  If (marketTime >= T) Then Exit Do
  currentRow = currentRow + 1
Loop

GetFirstRow = currentRow

End Function

GetLastRow is very similar.

My issue is that the GetFirstRow function has to process 49,000 (yes, forty nine thousand) rows, and it takes about 10 seconds.... so it takes "minutes" to complete this run.

Can someone help me optimize this?

Note I Need the date since market data starts the night before. If this is what is slowing me down, I can filter that as I import the data?

5
  • 2
    Use a Variant Array approach - lots of examples on SO Commented Aug 18, 2019 at 2:50
  • 1
    Best suited for codereview.stackexchange.com Commented Aug 18, 2019 at 2:52
  • Thanks for the pointer to using Variant arrays as a way to speed things up. 'Been looking at this for 30 minutes now and found no way to made a dynamic variant array public :). I wonder if I should just create a separate class to hold the array? Commented Aug 18, 2019 at 4:00
  • Just declare it at the top of a module in your workbook: Public myDynamicArray() As Variat and use the .Resize() property to map your data set so you can then loop through in memory, not a range. Commented Aug 18, 2019 at 7:39
  • Try using Maxifs Function in excel. You can call this function from VBA itself... corporatefinanceinstitute.com/resources/excel/functions/… Commented Aug 18, 2019 at 17:43

1 Answer 1

0

Following are the issues observed with the code

  1. Same loop in the function is used twice to get startRowNum and endRowNumthus doubling time
  2. No exit point in the function loop once the startRowNum and endRowNum are found. it is completing the loop till end
  3. It seems VBA is not all required for this purpose. It could be easily done with excel formula.
  4. If VBA loop is to be undertaken for any reason, then a single loop should extract all the required parameters in that single loop (may be multiple stocks). Test code below could be modified to adapt with existing code, since no function is used to avoid repeated use and thus slowing performance. The code tested with 260 K rows near bottom data and taking only 0.5 secs to calculate all four parameters.

Code used array

Option Explicit
Sub test()
Dim T1 As Date, T2 As Date
T1 = #8/12/2019 9:30:00 AM#
T2 = #8/12/2019 3:30:00 PM#

Dim PriceRange As Range, LastRow As Long
Dim MarketTime As Date
Dim Arr As Variant
Dim Rw As Long, StRow As Long
Dim tm As Double

Dim SRow As Long
Dim Erow As Long
Dim MaxPrice As Double
Dim MinPrice As Double

tm = Timer
With ThisWorkbook.ActiveSheet
StRow = 4
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row
Set PriceRange = .Range(.Cells(StRow, 2), .Cells(LastRow, 4))
Arr = PriceRange.Value
SRow = 0
Erow = 0
MaxPrice = -999999999
MinPrice = 999999999


Rw = 1
    Do While Rw <= UBound(Arr, 1)
    If IsDate(Arr(Rw, 1)) Then
    MarketTime = Arr(Rw, 1) + Arr(Rw, 2)
        If (MarketTime >= T1) And SRow = 0 Then SRow = Rw

        'If Rw Mod 1000 = 0 Then Debug.Print Rw, MarketTime, T1

        If SRow > 0 And Arr(Rw, 3) > MaxPrice Then
        MaxPrice = Arr(Rw, 3)
        End If

        If SRow > 0 And Arr(Rw, 3) < MinPrice Then
        MinPrice = Arr(Rw, 3)
        End If

        If (MarketTime >= T2) Then
        Erow = Rw
        Exit Do
        End If

    End If
    Rw = Rw + 1
    Loop
End With

Debug.Print SRow, Erow, MaxPrice, MinPrice
Debug.Print "Seconds taken " & Timer - tm
End Sub
Sign up to request clarification or add additional context in comments.

2 Comments

I love it. Thanks for the feedback. 1) My data is not currently in an array. It is in Cells. I'd love to put it into an array but I have not yet found a way to make an array public, especially a dynamic one (I read the data from a CSV file).... and 2) This may complicate things but for candlestick charts, I have to find min/max for every increment (like every 15 minutes throughout the day)... so optimizing is critical for me... thanks for the example above... I will see how to incorporate.
Cells are not arrays, but Ranges are.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.