VBA: Calculation gone wrong #VALUE - vba

I've been tasked with creating a Value at Risk function that can reference data in a range and a couple of cells. Code is:
Option Explicit
Function VaR(returns As Range, days, confidenceinterval, portfoliovalue)
VaR = (Application.WorksheetFunction.Average(returns) * Application.WorksheetFunction.SQRT(days)) - (Application.WorksheetFunction.NormSDist(confidenceinterval) * (Application.WorksheetFunction.StDev_S(returns) * Application.WorksheetFunction.SQRT(days))) * portfoliovalue
End Function
Returns is a range of returns, while the others are just values in cells.
As you can infer, i just copied my excel test formula, adding application.worksheet before each of the arguments. However, whereas my test formula works, the identical code translated in a format the VBA should recognised, does not.
The original formula is
=(AVERAGE(returns)*SQRT(days))-(NORMSDIST(confidenceinterval)*(STDEV.S(returns)*SQRT(days)))*portfoliovalue
Any help would be really excellent!
Thank you in advance!

You have two problems. First, your function name VaR conflicts with Excel's built-in VAR (variance) function. Second, there is no Application.WorksheetFunction.SQRT function; use VBA's Sqr instead. This should work:
Function ValueAtRisk(returns As Range, days, confidenceinterval, portfoliovalue)
With Application.WorksheetFunction
ValueAtRisk = (.Average(returns) * Sqr(days)) - (.NormSDist(confidenceinterval) * (.StDev_S(returns) * Sqr(days))) * portfoliovalue
End With
End Function
Hope that helps

If you want a VBA function to return a value the format to do so is
'Function functionName(parameters As parameterType) As returnType
So yours (assuming the types I've chosen are correct) should resolve to:
Function VaR(returns As Range, days As Integer, confidenceinterval as Double, portfoliovalue As Double) As Double
Based on Scott's suggestion here is one way that you might choose to break up and "step through" the formula that you're working on. If in your code window you show View > Locals Window you can use F8 to step through your code line by line you can see the values of part1 and part2 as they update from operation to operation.
Option Explicit
Function VaR(returns As Range, days As Integer, confidenceinterval As Double, portfoliovalue As Double) As Double
Dim part1 As Double
Dim part2 As Double
With Application.WorksheetFunction
part1 = .Average(returns) * .SQRT(days)
part2 = .NormSDist(confidenceinterval) * (.StDev_S(returns) * .SQRT(days))
VaR = part1 - part2 * portfoliovalue
End With
End Function
Since the test formula that you have to work with is
=(AVERAGE(returns)*SQRT(days))-(NORMSDIST(confidenceinterval)*(STDEV.S(returns)*SQRT(days)))*portfoliovalue
I would recommend splitting each part up so that you can compare, for example, my part1 to =(AVERAGE(returns)*SQRT(days)) and so on

Related

Suggestions for a user-defined function for significant figures and trailing zeros in Excel/VBA

I have written the user-defined function (UDF) below for an Excel sheet at work. It rounds to significant figure and is able to handle trailing zeros.
Actually, the UDF works as intended! The only thing, that you need to be aware off is that it convert the number to text.
However, I'm just a bit suspicious that I've overlooked something. Compared to the others functions, that I have found (Eg. https://www.vertex42.com/ExcelTips/significant-figures.html), it seems almost too simple.
Public Function ROUNDSF(num As Double, sigFig As Integer)
Dim sigPlace As Integer
Dim numFormat As String
sigPlace = sigFig - (1 + Int(Log(num) / Log(10)))
numFormat = "0." & String(sigPlace, "0")
ROUNDSF= Format(num, numFormat)
End Function
Are there anything, that I overlooked in this UDF? Or any suggestions?
If you want it to return a value, you could close the UDF with:
ROUNDSF = Val(Format(num, numFormat))
Keep in mind though, this will then use existing default formatting so make sure there is either none, or that it's compatible with your requirement.
As per Vincent's comment, if your user's locale settings might use something other than . as a decimal separator, use Cdbl:
ROUNDSF = CDbl(Format(num, numFormat))
You should also trap for -ve SigPlace values that could arise, like so:
Public Function ROUNDSF(num As Double, sigFig As Integer) As Double
Dim sigPlace As Integer
Dim numFormat As String
sigPlace = sigFig - (1 + Int(Log(num) / Log(10)))
If sigPlace < 0 Then sigPlace = 0
numFormat = "0." & String(sigPlace, "0")
ROUNDSF = Cdbl(Format(num, numFormat))
End Function

How to Truncate a Double in VBA

I have a variable in VBA that I need to truncate to 4 significant figures. I can't seem to find anything that won't round the number up or down. But I just want to remove the numbers after the 4th significant figure. I've tried,
compressibility = round(compress, -3 - (Int(Log(Abs(compress)))))
It removes the numbers after the 4th digit but it still rounds the number up.
Compress is a number around 0.000245848385 as an example, and I need the compressibility number to be 0.0002458.
Any suggestions would be great! Thanks.
Try this function:
Function RoundSignificant(ByVal dValue As Double, iFigures As Integer)
Dim dSig As Double
dSig = Abs(dValue)
dSig = Application.Log10(dSig)
dSig = 1 + Int(dSig)
dSig = iFigures - dSig
RoundSignificant = Round(dValue, dSig)
End Function
Sub test()
Debug.Print RoundSignificant(0.000245848385, 4)
End Sub
Using worksheet functions:
=VALUE(TEXT(compress,"0.000E+00"))
For VBA
CDbl(Format(compress,"0.000E+00"))
Hope that helps.
It seems to me that you want to avoid rounding UP, but not rounding down, since rounding down should produce the exact result you want. So, instead of using VBA Round function, you could use Excel WorksheetFunction.RoundDown method to achieve the result you need.
ROUNDDOWN(0.00024586548385;7)=0.000245800000
ROUNDDOWN(0.00024583548385;7)=0.000245800000
Sub test()
Dim compress As Double
compress = 0.000245858
Dim compressibility As Double
compressibility = Int(compress * 10 ^ -(Int(Log(Abs(compress))) - 3)) / 10 ^ -(Int(Log(Abs(compress))) - 3)
Debug.Print compressibility
End Sub

Speed up simple user defined functions

User defined functions writen in Excel VBA seem to run much slower than functions simply written in the worksheet cells. Is there a way to run them faster? As an example I have a very simple user defined function:
Function myweekday(mydate As Double)
myweekday = Weekday(mydate)
End Function
Basically it does the same thing as the built in =weekday() function. However running this on 50,000 cells takes about 5 seconds to calculate, whereas simply using the built in function takes a fraction of a second.
What can I do to make user defined functions like this run faster?
You can instead pass in the values as a range and have it return all the values at once in an array. I just tried this on 50,000 rows and it returned all the values instantly. You will need to enter the UDF with CTRL + SHIFT + ENTER.
Function myweekday(mydate As Range) As Variant
Dim vMydate As Variant
Dim vMyWeekDay As Variant
Dim i As Long
vMydate = mydate.Value2
ReDim vMyWeekDay(1 To UBound(vMydate), 1 To 1)
For i = 1 To UBound(vMydate)
vMyWeekDay(i, 1) = Weekday(vMydate(i, 1))
Next i
myweekday = vMyWeekDay
End Function

Count characters between two empty space to dashes() in vba

How do I get the length of character between beginning with space and ending with * Here is the image. Column B shows the total len before dasher(-) and my code
Sub xn()
Dim x As Integer
x = 1
If Worksheet("Sheet1").Range("A"& x).len(Right," ") Or _
Worksheet("Sheet1").Range("A"&x)len(Left,"-") Then
len(totallen)
End If
x = x + 1
End Sub
The code posted has multiple issues:
Worksheet is not a valid object - you need to use Worksheets.
.len is not a property of a Range object.
Even in .len was a property of a Range, you would need a
de-reference operator (aka '.') in here: Range("A"&x)len(Left,"-")
If you intend to use the function Len(), it only takes one argument.
You apparently are trying to loop, but you need to use either a For
or For Each loop - it won't loop automatically when you increment x
at the bottom of the sub.
Right is a function, but you're calling it without arguments and they are not optional.
Similarly, Left is a function, but you're also calling it without
the required arguments.
totallen is not declared anywhere, so Len(totallen) will assume
that totallen is a Variant (default for undeclared variables), then
cast it to a String, and then always return 0 because it has never
been given a value.
Anything else I may have missed.
The solution is to use the InStr function. It returns the location in a string of a given sub-string.
Sub xn()
Dim x As Long
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Worksheets("Sheet1")
For x = 1 To sheet.Range("A" & sheet.Rows.Count).End(xlUp).Row
sheet.Cells(x, 2) = InStr(1, sheet.Cells(x, 1), "-") - 1
Next x
End Sub
I'd also recommend taking a look at the MSDN article on Looping Through a Range of Cells (2003 vintage, but still valid), and Error Finding Last Used cell In VBA.

Functions not actualizing

I execute a VBA code that takes a database, treats it and export it into a sheet. This is working fine. However, I have a sheet that produces graphs depending on the data in the particular sheet. The datas does not actualize. I have to enter the cell and click enter to actualize it. I'm pretty sure there is an easier way to do this. Calculation is set to automatic but that doesn't seem to change anything.
In my cell, I have my own vba function that needs to be updated once the report is done. When I click the cell and then enter, the result is updated but I would like this to be done automatically. I hope this is clearer !
Thanks in advance,
Etienne NOEL
HEre is the code of my function
Public Function number_of_appearances(term As String, sheet As String, column As Integer) As Integer
Application.Volatile
Dim number_of_rows As Integer
Dim appearances As Integer
Dim row As Integer
appearances = 0
row = 1
number_of_rows = Worksheets(sheet).UsedRange.Rows.Count
Do While row <= number_of_rows
If Worksheets(sheet).Cells(row, column).Value = term Then
appearances = appearances + 1
End If
row = row + 1
Loop
number_of_appearances = appearances
End Function
A cell example of a user of the function
=number_of_appearances('test';'sheet1'; 3)
Sounds like your UDF might not depend on any cells that change value when your DB is processed.
See This MSDN Link
Post your UDF (or just its header if you prefer) and an example of its use...
EDIT:
Yes, none of the parameters to the UDF are cell references, therefore the UDF is not triggered to recalculate when data on the shet changes.
You have two choices:
1. rewrite your UDF to include parameter(s) that reference cells that change value when the DB is processed
2. make your UDF volitile (include Application.Volatile in the UDF code) WARNING: this can be very inefficient, depending on how many time the UDF is used and how intensive its calculation is
EDIT 2:
Heres a refactor of your udf using the first option mentioned:
Public Function number_of_appearances(term As String, rng As Range) As Integer
Dim v As Variant
Dim i As Long, j As Long
Dim appearances As Long
v = Intersect(rng, rng.Worksheet.UsedRange)
For j = LBound(v, 2) To UBound(v, 2)
For i = LBound(v, 1) To UBound(v, 1)
If v(i, j) = term Then
appearances = appearances + 1
End If
Next i, j
number_of_appearances = appearances
End Function
use like
=number_of_appearances("test";Sheet1!C:C)
EDIT 3:
If all you are doing is counting number of occurances of a string in a range, consider using
=COUNTIF(Sheet1!C:C;"test")