Closed yonil7 closed 3 years ago
@yonil7, so, this is similar to the first link I suggested in #7. Please, see http://westclintech.com/SQL-Server-Financial-Functions/SQL-Server-XFV-function
XFV
and XNFV
functions from the link above work for you, I can re-implement them in pyxirr
.XFV
function.I also believe it's better to have two distinct functions for regular XFV and irregular XFV.
XLeratorDB (the link you sent) has 2 relevant functions:
XFV
: calculate the future value of a single cash flowXNFV
: calculate the future value of a series of cash flows (N
is for "net". as the example on XFV
shows, this function is similar to SUMFV
that summarize multiple XFV
together)As all this library functions works on multiple cash flows (and not a single cash flow), XLeratorDB XNFV
is what we need. (but on pyxirr
I would name it XFV
- see comment on pyxirr
naming convention below)
As for the naming conventions on pyxirr
, I would use:
FV
, IRR
, PV
,... for periodic, constant paymentsXFV
, XIRR
, XPV
, ... for a series of irregular cash flows (cash flows of varying amounts occurring on various dates)@yonil7, XLeratorDB documentation says that XNPV
is the net future value of a series of irregular cash flows. This makes sense because we are using net present value for the calculation.
So far I have come up with the following functions:
from pyxirr import fv, xnpv, npv
# http://westclintech.com/SQL-Server-Financial-Functions/SQL-Server-XFV-function
def xfv(start_date, cash_flow_date, end_date, cash_flow_rate, end_rate, cash_flow):
return (
fv(end_rate, (end_date - start_date).days / 365, 0, -1)
/ fv(cash_flow_rate, (cash_flow_date - start_date).days / 365, 0, -1)
* cash_flow
)
# http://westclintech.com/SQL-Server-Financial-Functions/SQL-Server-XNFV-function
def xnfv(rate, dates, amounts):
periods = (max(dates) - min(dates)).days / 365
pv = xnpv(rate, dates, amounts)
return fv(rate, periods, 0, -pv)
# http://westclintech.com/SQL-Server-Financial-Functions/SQL-Server-NFV-function
def nfv(rate, nper, amounts):
pv = npv(rate, amounts, start_from_zero=False)
return fv(rate, nper, 0, -pv)
Examples:
Uneven periodic cash flows:
# net future value of an investment based on a series of periodic cash flows and a rate.
# example from https://www.youtube.com/watch?v=775ljhriB8U
print("NFV:", nfv(0.03, 6, [1050.0, 1350.0, 1350.0, 1450.0])) # 5750.16
Irregular cash flows:
from datetime import date
cash_flows = [
(date(2011, 11, 30), -100000),
(date(2012, 3, 15), -50000),
(date(2012, 7, 18), -2500),
(date(2012, 11, 30), 12500),
(date(2013, 1, 23), 37500),
(date(2013, 4, 30), 75000),
(date(2014, 2, 6), 90000),
]
dates, amounts = list(zip(*cash_flows))
start_date = min(dates)
end_date = max(dates)
rate = 0.0250 # annual
print("XNFV:", xnfv(rate, dates, amounts)) # 57238. must be the same as below
print(
"SUM(XFV):",
sum(
xfv(start_date, cash_flow_date, end_date, rate, rate, amount)
for cash_flow_date, amount in cash_flows
),
)
I'm going to include these functions in pyxirr
shortly.
on
XFV
instead of passing:we can pass:
Another (less explicit) option instead of passing
start_date
andend_date
is for the function to automatically take them from the series of dates passed (inamounts
ordates
) (start_date
will be the minimum date of this series,end_date
will be the maximum date of this series) In this case if the user will want to have astart_date
/end_date
that does not happen on the first/last payment, he/she can add these dates to the series with amount=0Also, we can add clarifications to the docs on when
rate
is per period (e.g. onFV
) and whenrate
is the annual interest rate (e.g. onXNPV
)