Main issue:
This nested loop of yours executes 975 * 260 = 253.500 times:
For col = 1 To 975
For n = 2 To 260
prices.Range("A:A").Offset(0, col).Copy stockreturns.Range("A:A").Offset(0, 2 * col + 1)
If stockreturns.Cells(n + 1, 2 * col).Value = Null Or IsEmpty(stockreturns.Cells(n + 1, 2 * col).Value) Then
stockreturns.Cells(n, 2 * col + 1) = Null
Else
stockreturns.Cells(n, 2 * col + 1).Formula = Cells(n, 2 * col) / Cells(n + 1, 2 * col) - 1
stockreturns.Cells(n, 2 * col + 1).NumberFormat = "0.00%"
End If
Next n
Next col
Summary of what you're doing, according to code in the question:
Basically, what you're doing is get column B, C, D, etc. and copy them to D, E, G, etc. using the offset. Next you check in the stockreturns worksheet what the value of the copied cell in the next row is (e.g. you check D3, then D4 etc.) and based on that populate E2, E3, etc. with nulls, or, alternatively you take ((D2 / D3) - 1) as a value there. The initial check is to avoid division by zero errors, I assume.
Note:
In those lines in your code you refer to Cells(n, 2 * col)
so that would always be the ActiveSheet
, whereas I assume you want to populate the worksheet stockreturns
with those values. I.e. if you run the formula with worksheet prices
activated, the formula's won't give the desired output.
Working towards solution:
For sure it would be way faster to not do 253.500 loops, but to populate everything at once for as far as possible. Since the column number varies everytime, we'll leave that loop in, but the nested 260 loops we can easily get rid of:
Optimization to do 975 loops instead of 253.500:
With stockreturns
For col = 1 To 975
prices.Range("A:A").Offset(0, col).Copy .Range("A:A").Offset(0, 2 * col + 1)
'Now we fill up the entire 260 rows at once using a relative formula:
.Range(.Cells(2, 2 * col + 1), .Cells(260, 2 * col + 1)).FormulaR1C1 = "=IF(R[+1]C[-1]="""","""",(RC[-1]/R[+1]C[-1])-1)"
'If you want a value instead of a formula, we replace the formula's with the value. If calculation is set to manual, you'll have to add an Application.Calculate here.
.Range(.Cells(2, 2 * col + 1), .Cells(260, 2 * col + 1)).Value = .Range(.Cells(2, 2 * col + 1), .Cells(260, 2 * col + 1)).Value
.Range(.Cells(2, 2 * col + 1), .Cells(260, 2 * col + 1)).NumberFormat = "0.00%"
Next col
End With
This will already save major execution time. However, we can also save ourselves 975 calculate actions, by turning off calculations and only replacing the formulas with the values at the very end:
Second optimization to avoid calculations during execution:
Application.Calculation = xlCalculationManual
With stockreturns
For col = 1 To 975
prices.Range("A:A").Offset(0, col).Copy .Range("A:A").Offset(0, 2 * col + 1)
'Now we fill up the entire 260 rows at once using a relative formula:
.Range(.Cells(2, 2 * col + 1), .Cells(260, 2 * col + 1)).FormulaR1C1 = "=IF(R[+1]C[-1]="""","""",(RC[-1]/R[+1]C[-1])-1)"
.Range(.Cells(2, 2 * col + 1), .Cells(260, 2 * col + 1)).NumberFormat = "0.00%"
Next col
End With
Application.Calculate
stockreturns.UsedRange.value = stockreturns.UsedRange.value
This last version runs in seconds.
If it is acceptable for you to alter the stockreturns
worksheet layout and use a continuous range to copy to at once, you won't need those 975 loops either but you can achieve the desired result with the following actions:
- Copy prices range
- Add the formula in another range
- Calculate
- Replace formulas with values
- Set numberformat
Hope this helps.