excel vba Ubound/Lbound error - vba

UBound seems to not be returning anything. I am using the Pricer function in excel and passing it a column. Did I mismatch my data types? I UBound on a dummy array that accessed arrP.Value and that didnt work either. Thoughts?
Function Pricer(arrP As Variant) As Double
sd = Application.WorksheetFunction.StDevP(arrP)
avg = Application.WorksheetFunction.Average(arrP)
PriceUB = avg + sd
PriceLB = avg - sd
MsgBox UBound(aarP)
Pricer = Application.WorksheetFunction.Average(arrP)
End Function

Option Explicit is exactly what would save you the next time you are suffering from "fat fingers". Just do not forget to declare the other variables:
Option Explicit
Public Sub TestMe()
MsgBox Pricer(Array(1, 2, 3, 4, 5, 100))
End Sub
Function Pricer(arrP As Variant) As Double
Dim sd, avg, PriceUB, PriceLB
sd = Application.WorksheetFunction.StDevP(arrP)
avg = Application.WorksheetFunction.Average(arrP)
PriceUB = avg + sd
PriceLB = avg - sd
MsgBox UBound(arrP)
Pricer = Application.WorksheetFunction.Average(arrP)
End Function
To use the formula in Excel worksheet, pass it like this:
The semicolon could be a comma, depending on your language settings:
To make the Pricer() work with Range() as an Excel formula this is what to do:
Function Pricer(arrP As Range) As Double
MsgBox arrP.Cells.Count
Pricer = Application.WorksheetFunction.Average(arrP)
End Function

Related

How to return 2 values and rounding them? Excel VBA

I am working on a code that should calculate simple foundations, and in order to do that I have to return 2 values with my function -preferably in two different columns.
Function FundacaoSimples(b, l, carga) As Variant
tensao = Sheets("Tabelas e Constantes").Range("tensao").Value
Dim area As Double
Dim Bs As Single
Dim Ls As Single
Dim Resultado(1 To 2) As String
If b = l Then
area = (1.1 * carga) / tensao
Bs = Sqr(area)
Ls = Bs
ElseIf b <> l Then
area = (1.1 * carga) / tensao
Bs = Sqr((2 * area) / 3)
Ls = (3 * Bs) / 2
End If
Resultado(1) = Round(Bs, 2)
Resultado(2) = Round(Ls, 2)
FundacaoSimples = (Resultado(1) & " / " & Resultado(2))
End Function
This rounding I am using it just to get a value rounded with 2 decimals, e.g: 2,73 to 2,75; 0,89 to 0,90.
I tried working with ActiveCells.Offset(0,1), but the statement isn't valid.
Is it possible to to just jump one column to the right?
You could use ActiveCell.Offset(0, 1).value = SomeValue, however - That's when writing a regular Sub. You're writing a Function / User Defined Function.
Within a UDF it is not possible to alter different cells.
However, a workaround is to have the UDF and when it's entered in a cell, you can then use the Worksheet_Change event to alter the cell next to the Target parameter of that event.
Edit:
Some sample code:
In a regular module:
Public Function MyUDF(param1 as integer, param2 as integer) as Integer
MyUDF = param1 + param2
End Function
In the Worksheet where you want the offset:
Private Sub Worksheet_Change(Byval Target as Range)
If Left(Target.Formula, 6) = "=MyUDF" Then
Target.Offset(0, 1).value = "somevalue at the offset cells"
End If
End Sub
In general, functions should not be writing values or accessing values from a spreadsheet. They should access their parameters and return result.
Try like this, an oversimplified version of what you need:
Option Explicit
Public Sub TestMe()
ActiveCell = FundacaoSimples(0)
ActiveCell.Offset(0, 1) = FundacaoSimples(1)
End Sub
Function FundacaoSimples() As Variant
ReDim varResult(1)
varResult(0) = 55
varResult(1) = 100
FundacaoSimples = varResult
End Function
Then you can edit the function a bit with your own parameters and use it further.

User-defined function returns non-formattable dates Excel VBA

I created a UDF in Excel VBA that returns an multi-dimensional array of dates and doubles. The problem is that I cannot format the dates that are returned.
Here is a simplified example:
Function test(dates as Range)
Dim results
Dim i As Integer
ReDim results(1 To dates.Cells.Count)
For i = 1 To dates.Cells.Count
results(i) = dates.Cells(i).Value
Next
test = Application.WorksheetFunction.Transpose(results)
End Function
The transpose at the end is just for convenience to have an column output (I press Ctrl+Shift+enter). I you use this simplified example, you will not be able to format the output and it will not be considered as dates stricto sensu.
Any ideas?
Change the results array to doubles:
Function test(dates As Range)
Dim results() As Double
Dim i As Integer
ReDim results(1 To dates.Cells.Count)
For i = 1 To dates.Cells.Count
results(i) = dates.Cells(i).Value
Next
test = Application.WorksheetFunction.Transpose(results)
End Function
Or change the dates.Cells(i).Value to dates.Cells(i).Value2 which will return the double not the date string:
Function test(dates As Range)
Dim results
Dim i As Integer
ReDim results(1 To dates.Cells.Count)
For i = 1 To dates.Cells.Count
results(i) = dates.Cells(i).Value2
Next
test = Application.WorksheetFunction.Transpose(results)
End Function
Then format the cells as you desire.
You may try something like this.
Results(i) = CDate(dates.Cells(i).Value)

how to call function to modify a variable vba

What my code does is it takes a value froma nother workbook and copies and paste it in another workbook. What I want now however is a something that would remove the last 3 figures to have a final number in thousand dollars instead of dollars.
It would work fine if I were to build my function around my "DSA.Worksheet.Value..." but it would take time because I have quite a few of those.
When I call my function on the DSA.worksheet cell it doesnt work, even if I call it like that:
Call number_right (DSA.Worksheets(1).Range("G10").Value = Application.WorksheetFunction.Round (hello.Worksheets("getthis"").Range("I72").Value, -3)
Sub test()
DSA.Worksheets(1).Range("G10").Value = Application.WorksheetFunction.Round(hello.Worksheets("getthis").Range("I72").Value, -3)
DSA.Worksheets(1).Range("G10").Value = Call number_right(DSA.Worksheets(1).Range("G10").Value)
End Sub
Function number_right(n As Variant) As Variant
If Len(n) > 3 Then n = Left((n), Len(n) - 3)
End Function
Maybe this:
Sub test()
Dim DSA As Workbook
Dim rngValue As Range
Set DSA = ActiveWorkbook 'May need to change this line if DSA is not already open
Set rngValue = DSA.Worksheets(1).Range("G10")
Set rngValue = Application.WorksheetFunction.Round(hello.Worksheets("getthis").Range("I72").Value, -3)
If Len(rngValue) > 3 Then
Set rngValue = number_right(rngValue)
End If
End Sub
Function number_right(n As Range) As Variant
number_right = Left(n, Len(n) - 3)
End Function

Excel formula calculating once then deleting

I have an excel formula:
=SplitKey(GetSysCd(INDEX([ReportValue],MATCH("mtr_make_model",[FieldName],0)),INDEX([ListName],MATCH("mtr_make_model",[FieldName],0))), 0)
which is running a few subroutines in VBA, but mainly matching values and inserting those values into a cell. When it finds a value for "mtr_make_model" it runs and matches the values inside a sys codes table. The issue I am having is that it is calculating once and then it removes the formula and now has solely the value... In the event that I go to the mtr_make_model field and change the value, the formula does not recalculate. Has anyone heard of this happening? Is this due to something in the VBA code? How do I make that formula stay put and if certain values change, the formula recalculates?
Thanks in advance.
Here are the two functions:
Public Function GetSysCd(ByVal name As String, sysCdTableName As String) As String
Dim r As Integer
Dim sysCdTable As Range
Dim nameList As Variant
Dim sysCd As String
On Error GoTo GetSysCd_Error
Set sysCdTable = Worksheets("sys_cd").Range(sysCdTableName)
nameList = WorksheetFunction.Index(sysCdTable, 0, 2)
r = WorksheetFunction.Match(name, nameList, 0)
sysCd = WorksheetFunction.Index(sysCdTable, r, 1)
GetOutOfHere:
On Error GoTo 0
GetSysCd = sysCd
Exit Function
GetSysCd_Error:
sysCd = ""
GoTo GetOutOfHere
End Function
Public Function SplitKey(s As String, v As Integer)
Dim aString As Variant
Dim r As Integer
If Len(s) > 2 Then
aString = Split(s, "_")
If v = 0 Or v = 1 Then
SplitKey = aString(v)
Else
SplitKey = aString(0)
End If
Else
SplitKey = ""
End If
End Function
I don't think the functions are relevant at this point, but rather just a matter of the function not recalculating when a variable in the formula changes...
The problem could be that Excel only recalculates functions when one of their arguments changes, and your GetSysCd function is referring to a range that is not in its argument list
Set sysCdTable = Worksheets("sys_cd").Range(sysCdTableName)
where sysCdTableName is just a string rather than a reference.
You can make the functions recalculate in real time by adding Application.Volatile True to the top of each function.

How to aggregate returns in an excel UDF using the product formula

I am trying to put the below formula into a UDF so that I can get a cumulative return when I aggregate monthly returns.
In excel the formula has to be recognized as an array so when I type it in I press Ctrl + Shift + Enter to get the {} brackets around the formula.
Does anyone know how to do this?
I want to be able to just type in returns_calc() and select the range that would fit into the returns variable below.
{=(PRODUCT(1+returns/100)-1)*100}
You can use the [ ] notation in Application.Evaluate to calculate Array Formulas in VBA. Your above formula can be called in VBA in just 1 line as shown below
Sub Sample()
MsgBox Application.Evaluate(["=(PRODUCT(1+returns/100)-1)*100"])
End Sub
Now modifying it to accept a range in a function, you may do this as well
Function returns_calc(rng As Range) As Variant
On Error GoTo Whoa
Dim frmulaStr As String
frmulaStr = "=(PRODUCT(1+(" & rng.Address & ")/100)-1)*100"
returns_calc = Application.Evaluate([frmulaStr])
Exit Function
Whoa:
returns_calc = "Please check formula string" 'or simply returns_calc = ""
End Function
EXAMPLE SCREENSHOT
Something like this
Public Function Range_Product(theRange As Variant)
Dim var As Variant
Dim j As Long
var = theRange.Value2
Range_Product = 1#
For j = LBound(var) To UBound(var)
Range_Product = Range_Product * (1 + var(j, 1) / 100)
Next j
Range_Product = (Range_Product - 1) * 100
End Function