Has anyone developed a financial function XNPV? I am looking for code that will calculate this value. I am not looking to call excel to calculate the value.
Any help would be great, thanks
I believe this is the correct implementation in c#:
public double XNPV(decimal[] receipts,DateTime[] dates, double dRate, DateTime issueDate, decimal cf, int x)
{
double sum;
for(int i =0;i<dates.Length;i++)
{
TimeSpan ts = dates[i].Subtract(issuedate);
sum +=receipts[i] / ((1 + dRate / cf) ^ ((ts.TotalDays / 360) * cf));
}
return sum;
}
and this is the equivalent in VB:
Public Function XNPV(ByVal Receipts() As Decimal, ByVal Dates() As Date, ByVal dRate As Double, ByVal IssueDate As Date, ByVal CF As Decimal, ByVal x As Integer) As Double
Dim i As Integer
Dim sum As Double
For i = 0 To UBound(Dates)
sum = sum + Receipts(i) / ((1 + dRate / CF) ^ ((DateDiff / 360) * CF))
XNPV = sum
End Function
I adapted the code above and included an example of how to call it. It should be easy to adapt to use a variable discount rate instead of .1
Public Function XNPV(ByVal Receipts() As Decimal, ByVal Dates() As Date, ByVal dRate As Double, ByVal StartDate As Date) As Double
Dim i As Integer = 0
Dim dXNPV As Double = 0
Dim dXRate As Double = 1 + dRate
Dim dDayOfCF As Long = 0
Dim dAdj As Double = 0
Dim dResult As Double = 0
For i = 0 To UBound(Dates)
dDayOfCF = (DateDiff(DateInterval.Day, StartDate, Dates(i)))
dAdj = (dDayOfCF / 365)
dXNPV = Receipts(i) / (dXRate ^ dAdj)
dResult += dXNPV
Next
XNPV = dResult
End Function
Private Sub SimpleButton2_Click(sender As System.Object, e As System.EventArgs) Handles SimpleButton2.Click
Dim tblReceipts(2) As Decimal
Dim tblDates(2) As DateTime
Dim dtStartDate As DateTime = "1/1/13"
Dim XNPVResult As Decimal = 0
tblReceipts(0) = -100000
tblReceipts(1) = 500000
tblReceipts(2) = 2000000
tblDates(0) = "1/1/13"
tblDates(1) = "1/7/13"
tblDates(2) = "1/1/15"
'xCF = 1 + DateDiff(DateInterval.Day, dtIssueDate, tblDates(2))
XNPVResult = XNPV(tblReceipts, tblDates, 0.1, dtStartDate)
MsgBox(XNPVResult)
End Sub
Returns value same as Excels' XNPV (double-checked)
public static double XNPV(double rate, List<xnpvFlow> Cashflows, DateTime? StartDate = null)
{
if (Cashflows == null || Cashflows.Count == 0) return 0;
if(StartDate == null)
{
StartDate = (from xnpvFlow flow in Cashflows select flow.FlowDate).Min();
}
double _xnpv = 0;
foreach(xnpvFlow flow in Cashflows)
{
_xnpv += (double)flow.FlowAmount / Math.Pow((1 + rate), (double)(flow.FlowDate - (DateTime)StartDate).Days/365);
}
return _xnpv;
}
public class xnpvFlow
{
public xnpvFlow(DateTime _FlowDate, decimal _flowAmount)
{
this.FlowAmount = _flowAmount;
this.FlowDate = _FlowDate;
}
public DateTime FlowDate;
public decimal FlowAmount;
}
Related
I have 2 strings that each contain 25 characters. E.g.
X = "0000111111110111111111110"
Y = "0000011111000000000000000"
What would be the most efficient method to identify, true or false if every position that has a "1" string Y also has a "1" in string X? In this example it should return True as there are 1s in X that match the positions of all 1s in Y.
I could read each character position and do a comparison for all 25 but was hoping some clever person would know of a more elegant way.
The easier way is to use Convert.ToInt32() to parse the string as a binary literal and perform binary AND:
Public Function MatchAsBinary(ByVal x As String, ByVal y As String) As Boolean
Dim x_int = Convert.ToInt32(x, 2)
Dim y_int = Convert.ToInt32(y, 2)
Return (x_int And y_int) = y_int
End Function
The faster (~10 times in release build) way is to compare the chars directly:
Public Function MatchAsChars(ByVal x As String, ByVal y As String) As Boolean
For i As Integer = 0 To y.Length - 1
If y(i) = "1"c AndAlso x(i) = "0"c Then
Return False
End If
Next
Return True
End Function
If you regard the strings as binary numbers, you can convert them to numbers and then use the bitwise and operator, like this:
Module Module1
Sub Main()
Dim X = "0000111111110111111111110"
Dim Y = "0000011111000000000000000"
Dim Xb = Convert.ToInt64(X, 2)
Dim Yb = Convert.ToInt64(Y, 2)
Console.WriteLine((Xb And Yb) = Yb)
Console.ReadLine()
End Sub
End Module
That will output True and work for strings of up to 64 characters.
Or, following on from your comment, you could use Convert.ToInt32 as that would give enough bits for your data.
Can do something similar #JoshD said above, but use Convert.ToInt32(Y, 2) to convert from a binary string to an integer.
Xint = Convert.ToInt32(X, 2)
Yint = Convert.ToInt32(Y, 2)
return ((Xint And Yint) = Yint)
This includes what others have shown plus a test for each bit one at a time.
Dim s As String = "0000011111000000000000000"
Dim X As String = "0000111111110111111111110"
Dim Y As String = "0000011111000000000000000"
Dim xi As Integer = Convert.ToInt32(X, 2)
Dim yi As Integer = Convert.ToInt32(Y, 2)
'check each bit
For i As Integer = 0 To 24
Dim msk As Integer = 1 << i
If (msk And xi) = msk AndAlso (msk And yi) = msk Then
Debug.WriteLine("Bit {0} on in both", i)
End If
Next
'all bits
Dim rslt As Integer = xi And yi
s = Convert.ToString(rslt, 2).PadLeft(25, "0"c)
Dim intY As Integer = CInt(Y)
Dim res As Boolean = (CInt(X) And intY) = intY
Convert them to integers, get all instances of matching 1's with a bitwise And, then compare to see if Y was changed by that comparison. If the comparison preserved the original Y, the result will be True.
I am getting a particular double number from the webservice like 0.097 or 0.034 from the webservice. So if i am getting a particular number like 0.56 or 0.5 i need to add a zero in 0.56 and two zeros in 0.5. How to do this in swift3?
Currently i am doing :
class func appendString(data: Int) -> String {
let value = data
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 3 // for float
formatter.maximumFractionDigits = 3 // for float
formatter.minimumIntegerDigits = 1
formatter.paddingPosition = .afterPrefix
formatter.paddingCharacter = "0"
return formatter.string(from: NSNumber(floatLiteral: Double(value)))!
}
Any idea how to achieve th above told logic?
The issue that you are passing data input in Int type, so it will always ignore fractions
see next example [Swift 3.1]
func formatNumber(_ number: Double) -> String? {
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 3 // minimum number of fraction digits on right
formatter.maximumFractionDigits = 3 // maximum number of fraction digits on right, or comment for all available
formatter.minimumIntegerDigits = 1 // minimum number of integer digits on left (necessary so that 0.5 don't return .500)
let formattedNumber = formatter.string(from: NSNumber.init(value: number))
return formattedNumber
}
let formattedNumber = formatNumber(0.5)
print(formattedNumber) // Optional("0.500")
Just few small changes to your code :
func appendString(data: Double) -> String { // changed input type of data
let value = data
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 3 // for float
formatter.maximumFractionDigits = 3 // for float
formatter.minimumIntegerDigits = 1
formatter.paddingPosition = .afterPrefix
formatter.paddingCharacter = "0"
return formatter.string(from: NSNumber(floatLiteral: value))! // here double() is not required as data is already double
}
print(appendString(data: 0.5)) // 0.500
Does it exist a way to avoid getting a rounded up result when using vlookup formula in vba. Here is my code:
Sub test()
Dim lastrow, pressurecolumn, lastcolumn As String, total As Integer, x, row, irow, column As Double
lastrow = Range("B8").End(xlDown).Value + 7
Range("Pump_design[Total Pipe losses from plantroom]").ClearContents
For Each row In Columns("EZ")
For irow = 8 To lastrow
total = 0
For column = 4 To 153
x = Cells(irow, column).Value
If Not IsEmpty(x) Then
total = total + Application.WorksheetFunction.VLookup(x, Sheets("Pump Design").Range("Pump_design"), 154, False)
End If
Next column
Cells(irow, "EZ") = Round(total, 4)
If Cells(irow, "EZ") = 0 Then Cells(irow, "EZ").ClearContents
Next irow
row = irow + 1
Next row
End Sub
Just found the trick get total as double instead of integer
so I've got this VBA code that calls DLL code. The DLL code works fine, the VBA code works fine UNTIL I go to call the DLL function from the VBA. For some reason it's not passing the 6th argument correctly. I tested by adding a 7th argument and passing the same value in the 6th and 7th arguments - the 7th passes fine, the 6th passes the same large (incorrect) value. I have no clue what is going on.
VBA:
Option Explicit
' Declare the LMM Function that's in the DLL
Declare PtrSafe Function GenCudaLMMPaths Lib "C:\Path to DLL\LMMExcel.dll" Alias "GenerateCUDALMMPaths" (xTimes#, xRates#, xVols#, xRData#, ByRef ArrLen As Long, ByRef NPaths As Long) As Long
' Generate LMM Paths on Click
Sub LMM_Click()
Dim Times#(), Rates#(), Vols#()
Dim x As Long
Dim y As Long
Dim rTimes As Range
Dim rRates As Range
Dim rVols As Range
Dim cell As Range
Dim sz As Long
sz = 15
' Resize
ReDim Times(sz), Rates(sz), Vols(sz)
' Fill in Data
Set rTimes = Sheets("Market").Range("C2:Q2")
x = 1
For Each cell In rTimes
Times(x) = cell.Value
x = x + 1
Next
Set rRates = Sheets("Market").Range("C5:Q5")
x = 1
For Each cell In rRates
Rates(x) = cell.Value
x = x + 1
Next
Set rVols = Sheets("Market").Range("C4:Q4")
x = 1
For Each cell In rVols
Vols(x) = cell.Value / 10000
x = x + 1
Next
'Call the Function
Dim np As Long
np = Sheets("LMM").Range("C2").Value
Dim useCuda As Boolean
If Sheets("LMM").Range("C3").Value = "GPU" Then
useCuda = True
Else
useCuda = False
End If
Dim rData#()
Dim rValue
ReDim rData(np * sz * (sz + 3))
rValue = GenCudaLMMPaths(Times(1), Rates(1), Vols(1), rData(1), sz, np)
If rValue = -1 Then
'No CUDA Card
MsgBox ("Your system doesn't have a CUDA Enabled GPU")
ElseIf rValue = 1 Then
'Error Occurred
MsgBox ("An error occurred while trying to generate LMM paths")
ElseIf rValue = 0 Then
'Success
' Need to reformat return data
Dim fmtData()
ReDim fmtData(np * sz, sz)
Dim i, j, k
For i = 0 To np - 1
For j = 0 To np - 1
For k = 0 To np - 1
fmtData(((i * sz) + j) + 1, k + 1) = rData(((i * sz * sz) + (j * sz) + k) + 1)
Next k
Next j
Next i
'Fill in data
Sheets("LMM").Range("A8:K" & (np * sz)) = fmtData
Else
'Too many requested paths for this CUDA card
MsgBox ("In order to prevent GPU Lock-up, you cannot request more than " & rValue & " paths.")
Sheets("LMM").Range("C2").Value = rValue
End If
End Sub
DLL Function Declaration:
int __stdcall GenerateCUDALMMPaths(double* arrTimes, double* arrRates, double* arrVols, double* retData, int& ArrLength, int& NPaths);
DEF File:
LIBRARY "CUDAFinance"
EXPORTS
CheckExcelArray = CheckExcelArray
GenerateLMMPaths = GenerateLMMPaths
GenerateCUDALMMPaths = GenerateCUDALMMPaths
Anyone have any idea here? I'm completely lost.
I just run into the same problem and got it solved as follows.
Since you already have a long variable in the six arguments function, import the NPaths together with Arrlen as an array without adding a 7th argument:
1) In VBA:
Declare a two elements array:
Dim NArrLenNPaths(1) as long
Then, assign values:
NArrLenNPaths(0) contains ArrLen and NArrLenNPaths(1) the NPaths value.
Keep the function delcaration in VBA but when calling it put NArrLenNPaths(0) as 6th argument. Do not put a 7th argument. The C++ will retreive both values as follows.
2) In C++ use a pointer instead:
Change the 6th argument to
int* NArrLenNPaths
then retreive the values by
int NArrLen = NArrLenNPaths[0];
int NPaths = NArrLenNPaths[1];
I have a "container" containing data. The size is +- 100MB.
In the container there a several "dataids's" that mark the begin of something.
Now I need to get an index for an given dataid. (dataid for example: '4CFE7197-0029-006B-1AD4-000000000012')
I have tried several approaches. But at this moment "ReadAllBytes" is the most performant.
ReadAll -> average of 0.6 seconds
Using oReader As New BinaryReader(File.Open(sContainerPath, FileMode.Open, FileAccess.Read))
Dim iLength As Integer = CInt(oReader.BaseStream.Length)
Dim oValue As Byte() = Nothing
oValue = oReader.ReadBytes(iLength)
Dim enc As New System.Text.ASCIIEncoding
Dim sFileContent As String = enc.GetString(oValue)
Dim r As Regex = New Regex(sDataId)
Dim lPosArcID As Integer = r.Match(sFileContent).Index
If lPosArcID > 0 Then
Return lPosArcID
End If
End Using
ReadByteByByte -> average of 1.4 seconds
Using oReader As BinaryReader = New BinaryReader(File.Open(sContainerPath, FileMode.Open, FileAccess.Read))
Dim valueSearch As StringSearch = New StringSearch(sDataId)
Dim readByte As Byte
While (InlineAssignHelper(readByte, oReader.ReadByte()) >= 0)
index += 1
If valueSearch.Found(readByte) Then
Return index - iDataIdLength
End If
End While
End Using
Public Class StringSearch
Private ReadOnly oValue() As Byte
Private iValueIndex As Integer = -1
Public Sub New(value As String)
Dim oEncoding As New System.Text.ASCIIEncoding
Me.oValue = oEncoding.GetBytes(value)
End Sub
Public Function Found(oNextByte As Byte) As Boolean
If oValue(iValueIndex + 1) = oNextByte Then
iValueIndex += 1
If iValueIndex + 1 = oValue.Count Then Return True
Else
iValueIndex = -1
End If
Return False
End Function
End Class
Public Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
I find it hard to believe that there is no faster way.
0.6 seconds for a 100MB file is not an acceptable time.
An other approach that I tried, is to split in chuncks of X bytes (100, 1000, ..). But was alot slower.
Any help on an approach I can try?