annotate DEPENDENCIES/mingw32/Python27/Lib/site-packages/numpy/lib/financial.py @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents 2a2c65a20a8b
children
rev   line source
Chris@87 1 """Some simple financial calculations
Chris@87 2
Chris@87 3 patterned after spreadsheet computations.
Chris@87 4
Chris@87 5 There is some complexity in each function
Chris@87 6 so that the functions behave like ufuncs with
Chris@87 7 broadcasting and being able to be called with scalars
Chris@87 8 or arrays (or other sequences).
Chris@87 9
Chris@87 10 """
Chris@87 11 from __future__ import division, absolute_import, print_function
Chris@87 12
Chris@87 13 import numpy as np
Chris@87 14
Chris@87 15 __all__ = ['fv', 'pmt', 'nper', 'ipmt', 'ppmt', 'pv', 'rate',
Chris@87 16 'irr', 'npv', 'mirr']
Chris@87 17
Chris@87 18 _when_to_num = {'end':0, 'begin':1,
Chris@87 19 'e':0, 'b':1,
Chris@87 20 0:0, 1:1,
Chris@87 21 'beginning':1,
Chris@87 22 'start':1,
Chris@87 23 'finish':0}
Chris@87 24
Chris@87 25 def _convert_when(when):
Chris@87 26 #Test to see if when has already been converted to ndarray
Chris@87 27 #This will happen if one function calls another, for example ppmt
Chris@87 28 if isinstance(when, np.ndarray):
Chris@87 29 return when
Chris@87 30 try:
Chris@87 31 return _when_to_num[when]
Chris@87 32 except (KeyError, TypeError):
Chris@87 33 return [_when_to_num[x] for x in when]
Chris@87 34
Chris@87 35
Chris@87 36 def fv(rate, nper, pmt, pv, when='end'):
Chris@87 37 """
Chris@87 38 Compute the future value.
Chris@87 39
Chris@87 40 Given:
Chris@87 41 * a present value, `pv`
Chris@87 42 * an interest `rate` compounded once per period, of which
Chris@87 43 there are
Chris@87 44 * `nper` total
Chris@87 45 * a (fixed) payment, `pmt`, paid either
Chris@87 46 * at the beginning (`when` = {'begin', 1}) or the end
Chris@87 47 (`when` = {'end', 0}) of each period
Chris@87 48
Chris@87 49 Return:
Chris@87 50 the value at the end of the `nper` periods
Chris@87 51
Chris@87 52 Parameters
Chris@87 53 ----------
Chris@87 54 rate : scalar or array_like of shape(M, )
Chris@87 55 Rate of interest as decimal (not per cent) per period
Chris@87 56 nper : scalar or array_like of shape(M, )
Chris@87 57 Number of compounding periods
Chris@87 58 pmt : scalar or array_like of shape(M, )
Chris@87 59 Payment
Chris@87 60 pv : scalar or array_like of shape(M, )
Chris@87 61 Present value
Chris@87 62 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional
Chris@87 63 When payments are due ('begin' (1) or 'end' (0)).
Chris@87 64 Defaults to {'end', 0}.
Chris@87 65
Chris@87 66 Returns
Chris@87 67 -------
Chris@87 68 out : ndarray
Chris@87 69 Future values. If all input is scalar, returns a scalar float. If
Chris@87 70 any input is array_like, returns future values for each input element.
Chris@87 71 If multiple inputs are array_like, they all must have the same shape.
Chris@87 72
Chris@87 73 Notes
Chris@87 74 -----
Chris@87 75 The future value is computed by solving the equation::
Chris@87 76
Chris@87 77 fv +
Chris@87 78 pv*(1+rate)**nper +
Chris@87 79 pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) == 0
Chris@87 80
Chris@87 81 or, when ``rate == 0``::
Chris@87 82
Chris@87 83 fv + pv + pmt * nper == 0
Chris@87 84
Chris@87 85 References
Chris@87 86 ----------
Chris@87 87 .. [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
Chris@87 88 Open Document Format for Office Applications (OpenDocument)v1.2,
Chris@87 89 Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Chris@87 90 Pre-Draft 12. Organization for the Advancement of Structured Information
Chris@87 91 Standards (OASIS). Billerica, MA, USA. [ODT Document].
Chris@87 92 Available:
Chris@87 93 http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula
Chris@87 94 OpenDocument-formula-20090508.odt
Chris@87 95
Chris@87 96 Examples
Chris@87 97 --------
Chris@87 98 What is the future value after 10 years of saving $100 now, with
Chris@87 99 an additional monthly savings of $100. Assume the interest rate is
Chris@87 100 5% (annually) compounded monthly?
Chris@87 101
Chris@87 102 >>> np.fv(0.05/12, 10*12, -100, -100)
Chris@87 103 15692.928894335748
Chris@87 104
Chris@87 105 By convention, the negative sign represents cash flow out (i.e. money not
Chris@87 106 available today). Thus, saving $100 a month at 5% annual interest leads
Chris@87 107 to $15,692.93 available to spend in 10 years.
Chris@87 108
Chris@87 109 If any input is array_like, returns an array of equal shape. Let's
Chris@87 110 compare different interest rates from the example above.
Chris@87 111
Chris@87 112 >>> a = np.array((0.05, 0.06, 0.07))/12
Chris@87 113 >>> np.fv(a, 10*12, -100, -100)
Chris@87 114 array([ 15692.92889434, 16569.87435405, 17509.44688102])
Chris@87 115
Chris@87 116 """
Chris@87 117 when = _convert_when(when)
Chris@87 118 (rate, nper, pmt, pv, when) = map(np.asarray, [rate, nper, pmt, pv, when])
Chris@87 119 temp = (1+rate)**nper
Chris@87 120 miter = np.broadcast(rate, nper, pmt, pv, when)
Chris@87 121 zer = np.zeros(miter.shape)
Chris@87 122 fact = np.where(rate == zer, nper + zer,
Chris@87 123 (1 + rate*when)*(temp - 1)/rate + zer)
Chris@87 124 return -(pv*temp + pmt*fact)
Chris@87 125
Chris@87 126 def pmt(rate, nper, pv, fv=0, when='end'):
Chris@87 127 """
Chris@87 128 Compute the payment against loan principal plus interest.
Chris@87 129
Chris@87 130 Given:
Chris@87 131 * a present value, `pv` (e.g., an amount borrowed)
Chris@87 132 * a future value, `fv` (e.g., 0)
Chris@87 133 * an interest `rate` compounded once per period, of which
Chris@87 134 there are
Chris@87 135 * `nper` total
Chris@87 136 * and (optional) specification of whether payment is made
Chris@87 137 at the beginning (`when` = {'begin', 1}) or the end
Chris@87 138 (`when` = {'end', 0}) of each period
Chris@87 139
Chris@87 140 Return:
Chris@87 141 the (fixed) periodic payment.
Chris@87 142
Chris@87 143 Parameters
Chris@87 144 ----------
Chris@87 145 rate : array_like
Chris@87 146 Rate of interest (per period)
Chris@87 147 nper : array_like
Chris@87 148 Number of compounding periods
Chris@87 149 pv : array_like
Chris@87 150 Present value
Chris@87 151 fv : array_like (optional)
Chris@87 152 Future value (default = 0)
Chris@87 153 when : {{'begin', 1}, {'end', 0}}, {string, int}
Chris@87 154 When payments are due ('begin' (1) or 'end' (0))
Chris@87 155
Chris@87 156 Returns
Chris@87 157 -------
Chris@87 158 out : ndarray
Chris@87 159 Payment against loan plus interest. If all input is scalar, returns a
Chris@87 160 scalar float. If any input is array_like, returns payment for each
Chris@87 161 input element. If multiple inputs are array_like, they all must have
Chris@87 162 the same shape.
Chris@87 163
Chris@87 164 Notes
Chris@87 165 -----
Chris@87 166 The payment is computed by solving the equation::
Chris@87 167
Chris@87 168 fv +
Chris@87 169 pv*(1 + rate)**nper +
Chris@87 170 pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) == 0
Chris@87 171
Chris@87 172 or, when ``rate == 0``::
Chris@87 173
Chris@87 174 fv + pv + pmt * nper == 0
Chris@87 175
Chris@87 176 for ``pmt``.
Chris@87 177
Chris@87 178 Note that computing a monthly mortgage payment is only
Chris@87 179 one use for this function. For example, pmt returns the
Chris@87 180 periodic deposit one must make to achieve a specified
Chris@87 181 future balance given an initial deposit, a fixed,
Chris@87 182 periodically compounded interest rate, and the total
Chris@87 183 number of periods.
Chris@87 184
Chris@87 185 References
Chris@87 186 ----------
Chris@87 187 .. [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
Chris@87 188 Open Document Format for Office Applications (OpenDocument)v1.2,
Chris@87 189 Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Chris@87 190 Pre-Draft 12. Organization for the Advancement of Structured Information
Chris@87 191 Standards (OASIS). Billerica, MA, USA. [ODT Document].
Chris@87 192 Available:
Chris@87 193 http://www.oasis-open.org/committees/documents.php
Chris@87 194 ?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt
Chris@87 195
Chris@87 196 Examples
Chris@87 197 --------
Chris@87 198 What is the monthly payment needed to pay off a $200,000 loan in 15
Chris@87 199 years at an annual interest rate of 7.5%?
Chris@87 200
Chris@87 201 >>> np.pmt(0.075/12, 12*15, 200000)
Chris@87 202 -1854.0247200054619
Chris@87 203
Chris@87 204 In order to pay-off (i.e., have a future-value of 0) the $200,000 obtained
Chris@87 205 today, a monthly payment of $1,854.02 would be required. Note that this
Chris@87 206 example illustrates usage of `fv` having a default value of 0.
Chris@87 207
Chris@87 208 """
Chris@87 209 when = _convert_when(when)
Chris@87 210 (rate, nper, pv, fv, when) = map(np.asarray, [rate, nper, pv, fv, when])
Chris@87 211 temp = (1+rate)**nper
Chris@87 212 miter = np.broadcast(rate, nper, pv, fv, when)
Chris@87 213 zer = np.zeros(miter.shape)
Chris@87 214 fact = np.where(rate == zer, nper + zer,
Chris@87 215 (1 + rate*when)*(temp - 1)/rate + zer)
Chris@87 216 return -(fv + pv*temp) / fact
Chris@87 217
Chris@87 218 def nper(rate, pmt, pv, fv=0, when='end'):
Chris@87 219 """
Chris@87 220 Compute the number of periodic payments.
Chris@87 221
Chris@87 222 Parameters
Chris@87 223 ----------
Chris@87 224 rate : array_like
Chris@87 225 Rate of interest (per period)
Chris@87 226 pmt : array_like
Chris@87 227 Payment
Chris@87 228 pv : array_like
Chris@87 229 Present value
Chris@87 230 fv : array_like, optional
Chris@87 231 Future value
Chris@87 232 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional
Chris@87 233 When payments are due ('begin' (1) or 'end' (0))
Chris@87 234
Chris@87 235 Notes
Chris@87 236 -----
Chris@87 237 The number of periods ``nper`` is computed by solving the equation::
Chris@87 238
Chris@87 239 fv + pv*(1+rate)**nper + pmt*(1+rate*when)/rate*((1+rate)**nper-1) = 0
Chris@87 240
Chris@87 241 but if ``rate = 0`` then::
Chris@87 242
Chris@87 243 fv + pv + pmt*nper = 0
Chris@87 244
Chris@87 245 Examples
Chris@87 246 --------
Chris@87 247 If you only had $150/month to pay towards the loan, how long would it take
Chris@87 248 to pay-off a loan of $8,000 at 7% annual interest?
Chris@87 249
Chris@87 250 >>> print round(np.nper(0.07/12, -150, 8000), 5)
Chris@87 251 64.07335
Chris@87 252
Chris@87 253 So, over 64 months would be required to pay off the loan.
Chris@87 254
Chris@87 255 The same analysis could be done with several different interest rates
Chris@87 256 and/or payments and/or total amounts to produce an entire table.
Chris@87 257
Chris@87 258 >>> np.nper(*(np.ogrid[0.07/12: 0.08/12: 0.01/12,
Chris@87 259 ... -150 : -99 : 50 ,
Chris@87 260 ... 8000 : 9001 : 1000]))
Chris@87 261 array([[[ 64.07334877, 74.06368256],
Chris@87 262 [ 108.07548412, 127.99022654]],
Chris@87 263 [[ 66.12443902, 76.87897353],
Chris@87 264 [ 114.70165583, 137.90124779]]])
Chris@87 265
Chris@87 266 """
Chris@87 267 when = _convert_when(when)
Chris@87 268 (rate, pmt, pv, fv, when) = map(np.asarray, [rate, pmt, pv, fv, when])
Chris@87 269
Chris@87 270 use_zero_rate = False
Chris@87 271 with np.errstate(divide="raise"):
Chris@87 272 try:
Chris@87 273 z = pmt*(1.0+rate*when)/rate
Chris@87 274 except FloatingPointError:
Chris@87 275 use_zero_rate = True
Chris@87 276
Chris@87 277 if use_zero_rate:
Chris@87 278 return (-fv + pv) / (pmt + 0.0)
Chris@87 279 else:
Chris@87 280 A = -(fv + pv)/(pmt+0.0)
Chris@87 281 B = np.log((-fv+z) / (pv+z))/np.log(1.0+rate)
Chris@87 282 miter = np.broadcast(rate, pmt, pv, fv, when)
Chris@87 283 zer = np.zeros(miter.shape)
Chris@87 284 return np.where(rate == zer, A + zer, B + zer) + 0.0
Chris@87 285
Chris@87 286 def ipmt(rate, per, nper, pv, fv=0.0, when='end'):
Chris@87 287 """
Chris@87 288 Compute the interest portion of a payment.
Chris@87 289
Chris@87 290 Parameters
Chris@87 291 ----------
Chris@87 292 rate : scalar or array_like of shape(M, )
Chris@87 293 Rate of interest as decimal (not per cent) per period
Chris@87 294 per : scalar or array_like of shape(M, )
Chris@87 295 Interest paid against the loan changes during the life or the loan.
Chris@87 296 The `per` is the payment period to calculate the interest amount.
Chris@87 297 nper : scalar or array_like of shape(M, )
Chris@87 298 Number of compounding periods
Chris@87 299 pv : scalar or array_like of shape(M, )
Chris@87 300 Present value
Chris@87 301 fv : scalar or array_like of shape(M, ), optional
Chris@87 302 Future value
Chris@87 303 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional
Chris@87 304 When payments are due ('begin' (1) or 'end' (0)).
Chris@87 305 Defaults to {'end', 0}.
Chris@87 306
Chris@87 307 Returns
Chris@87 308 -------
Chris@87 309 out : ndarray
Chris@87 310 Interest portion of payment. If all input is scalar, returns a scalar
Chris@87 311 float. If any input is array_like, returns interest payment for each
Chris@87 312 input element. If multiple inputs are array_like, they all must have
Chris@87 313 the same shape.
Chris@87 314
Chris@87 315 See Also
Chris@87 316 --------
Chris@87 317 ppmt, pmt, pv
Chris@87 318
Chris@87 319 Notes
Chris@87 320 -----
Chris@87 321 The total payment is made up of payment against principal plus interest.
Chris@87 322
Chris@87 323 ``pmt = ppmt + ipmt``
Chris@87 324
Chris@87 325 Examples
Chris@87 326 --------
Chris@87 327 What is the amortization schedule for a 1 year loan of $2500 at
Chris@87 328 8.24% interest per year compounded monthly?
Chris@87 329
Chris@87 330 >>> principal = 2500.00
Chris@87 331
Chris@87 332 The 'per' variable represents the periods of the loan. Remember that
Chris@87 333 financial equations start the period count at 1!
Chris@87 334
Chris@87 335 >>> per = np.arange(1*12) + 1
Chris@87 336 >>> ipmt = np.ipmt(0.0824/12, per, 1*12, principal)
Chris@87 337 >>> ppmt = np.ppmt(0.0824/12, per, 1*12, principal)
Chris@87 338
Chris@87 339 Each element of the sum of the 'ipmt' and 'ppmt' arrays should equal
Chris@87 340 'pmt'.
Chris@87 341
Chris@87 342 >>> pmt = np.pmt(0.0824/12, 1*12, principal)
Chris@87 343 >>> np.allclose(ipmt + ppmt, pmt)
Chris@87 344 True
Chris@87 345
Chris@87 346 >>> fmt = '{0:2d} {1:8.2f} {2:8.2f} {3:8.2f}'
Chris@87 347 >>> for payment in per:
Chris@87 348 ... index = payment - 1
Chris@87 349 ... principal = principal + ppmt[index]
Chris@87 350 ... print fmt.format(payment, ppmt[index], ipmt[index], principal)
Chris@87 351 1 -200.58 -17.17 2299.42
Chris@87 352 2 -201.96 -15.79 2097.46
Chris@87 353 3 -203.35 -14.40 1894.11
Chris@87 354 4 -204.74 -13.01 1689.37
Chris@87 355 5 -206.15 -11.60 1483.22
Chris@87 356 6 -207.56 -10.18 1275.66
Chris@87 357 7 -208.99 -8.76 1066.67
Chris@87 358 8 -210.42 -7.32 856.25
Chris@87 359 9 -211.87 -5.88 644.38
Chris@87 360 10 -213.32 -4.42 431.05
Chris@87 361 11 -214.79 -2.96 216.26
Chris@87 362 12 -216.26 -1.49 -0.00
Chris@87 363
Chris@87 364 >>> interestpd = np.sum(ipmt)
Chris@87 365 >>> np.round(interestpd, 2)
Chris@87 366 -112.98
Chris@87 367
Chris@87 368 """
Chris@87 369 when = _convert_when(when)
Chris@87 370 rate, per, nper, pv, fv, when = np.broadcast_arrays(rate, per, nper,
Chris@87 371 pv, fv, when)
Chris@87 372 total_pmt = pmt(rate, nper, pv, fv, when)
Chris@87 373 ipmt = _rbl(rate, per, total_pmt, pv, when)*rate
Chris@87 374 try:
Chris@87 375 ipmt = np.where(when == 1, ipmt/(1 + rate), ipmt)
Chris@87 376 ipmt = np.where(np.logical_and(when == 1, per == 1), 0.0, ipmt)
Chris@87 377 except IndexError:
Chris@87 378 pass
Chris@87 379 return ipmt
Chris@87 380
Chris@87 381 def _rbl(rate, per, pmt, pv, when):
Chris@87 382 """
Chris@87 383 This function is here to simply have a different name for the 'fv'
Chris@87 384 function to not interfere with the 'fv' keyword argument within the 'ipmt'
Chris@87 385 function. It is the 'remaining balance on loan' which might be useful as
Chris@87 386 it's own function, but is easily calculated with the 'fv' function.
Chris@87 387 """
Chris@87 388 return fv(rate, (per - 1), pmt, pv, when)
Chris@87 389
Chris@87 390 def ppmt(rate, per, nper, pv, fv=0.0, when='end'):
Chris@87 391 """
Chris@87 392 Compute the payment against loan principal.
Chris@87 393
Chris@87 394 Parameters
Chris@87 395 ----------
Chris@87 396 rate : array_like
Chris@87 397 Rate of interest (per period)
Chris@87 398 per : array_like, int
Chris@87 399 Amount paid against the loan changes. The `per` is the period of
Chris@87 400 interest.
Chris@87 401 nper : array_like
Chris@87 402 Number of compounding periods
Chris@87 403 pv : array_like
Chris@87 404 Present value
Chris@87 405 fv : array_like, optional
Chris@87 406 Future value
Chris@87 407 when : {{'begin', 1}, {'end', 0}}, {string, int}
Chris@87 408 When payments are due ('begin' (1) or 'end' (0))
Chris@87 409
Chris@87 410 See Also
Chris@87 411 --------
Chris@87 412 pmt, pv, ipmt
Chris@87 413
Chris@87 414 """
Chris@87 415 total = pmt(rate, nper, pv, fv, when)
Chris@87 416 return total - ipmt(rate, per, nper, pv, fv, when)
Chris@87 417
Chris@87 418 def pv(rate, nper, pmt, fv=0.0, when='end'):
Chris@87 419 """
Chris@87 420 Compute the present value.
Chris@87 421
Chris@87 422 Given:
Chris@87 423 * a future value, `fv`
Chris@87 424 * an interest `rate` compounded once per period, of which
Chris@87 425 there are
Chris@87 426 * `nper` total
Chris@87 427 * a (fixed) payment, `pmt`, paid either
Chris@87 428 * at the beginning (`when` = {'begin', 1}) or the end
Chris@87 429 (`when` = {'end', 0}) of each period
Chris@87 430
Chris@87 431 Return:
Chris@87 432 the value now
Chris@87 433
Chris@87 434 Parameters
Chris@87 435 ----------
Chris@87 436 rate : array_like
Chris@87 437 Rate of interest (per period)
Chris@87 438 nper : array_like
Chris@87 439 Number of compounding periods
Chris@87 440 pmt : array_like
Chris@87 441 Payment
Chris@87 442 fv : array_like, optional
Chris@87 443 Future value
Chris@87 444 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional
Chris@87 445 When payments are due ('begin' (1) or 'end' (0))
Chris@87 446
Chris@87 447 Returns
Chris@87 448 -------
Chris@87 449 out : ndarray, float
Chris@87 450 Present value of a series of payments or investments.
Chris@87 451
Chris@87 452 Notes
Chris@87 453 -----
Chris@87 454 The present value is computed by solving the equation::
Chris@87 455
Chris@87 456 fv +
Chris@87 457 pv*(1 + rate)**nper +
Chris@87 458 pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) = 0
Chris@87 459
Chris@87 460 or, when ``rate = 0``::
Chris@87 461
Chris@87 462 fv + pv + pmt * nper = 0
Chris@87 463
Chris@87 464 for `pv`, which is then returned.
Chris@87 465
Chris@87 466 References
Chris@87 467 ----------
Chris@87 468 .. [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
Chris@87 469 Open Document Format for Office Applications (OpenDocument)v1.2,
Chris@87 470 Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Chris@87 471 Pre-Draft 12. Organization for the Advancement of Structured Information
Chris@87 472 Standards (OASIS). Billerica, MA, USA. [ODT Document].
Chris@87 473 Available:
Chris@87 474 http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula
Chris@87 475 OpenDocument-formula-20090508.odt
Chris@87 476
Chris@87 477 Examples
Chris@87 478 --------
Chris@87 479 What is the present value (e.g., the initial investment)
Chris@87 480 of an investment that needs to total $15692.93
Chris@87 481 after 10 years of saving $100 every month? Assume the
Chris@87 482 interest rate is 5% (annually) compounded monthly.
Chris@87 483
Chris@87 484 >>> np.pv(0.05/12, 10*12, -100, 15692.93)
Chris@87 485 -100.00067131625819
Chris@87 486
Chris@87 487 By convention, the negative sign represents cash flow out
Chris@87 488 (i.e., money not available today). Thus, to end up with
Chris@87 489 $15,692.93 in 10 years saving $100 a month at 5% annual
Chris@87 490 interest, one's initial deposit should also be $100.
Chris@87 491
Chris@87 492 If any input is array_like, ``pv`` returns an array of equal shape.
Chris@87 493 Let's compare different interest rates in the example above:
Chris@87 494
Chris@87 495 >>> a = np.array((0.05, 0.04, 0.03))/12
Chris@87 496 >>> np.pv(a, 10*12, -100, 15692.93)
Chris@87 497 array([ -100.00067132, -649.26771385, -1273.78633713])
Chris@87 498
Chris@87 499 So, to end up with the same $15692.93 under the same $100 per month
Chris@87 500 "savings plan," for annual interest rates of 4% and 3%, one would
Chris@87 501 need initial investments of $649.27 and $1273.79, respectively.
Chris@87 502
Chris@87 503 """
Chris@87 504 when = _convert_when(when)
Chris@87 505 (rate, nper, pmt, fv, when) = map(np.asarray, [rate, nper, pmt, fv, when])
Chris@87 506 temp = (1+rate)**nper
Chris@87 507 miter = np.broadcast(rate, nper, pmt, fv, when)
Chris@87 508 zer = np.zeros(miter.shape)
Chris@87 509 fact = np.where(rate == zer, nper+zer, (1+rate*when)*(temp-1)/rate+zer)
Chris@87 510 return -(fv + pmt*fact)/temp
Chris@87 511
Chris@87 512 # Computed with Sage
Chris@87 513 # (y + (r + 1)^n*x + p*((r + 1)^n - 1)*(r*w + 1)/r)/(n*(r + 1)^(n - 1)*x -
Chris@87 514 # p*((r + 1)^n - 1)*(r*w + 1)/r^2 + n*p*(r + 1)^(n - 1)*(r*w + 1)/r +
Chris@87 515 # p*((r + 1)^n - 1)*w/r)
Chris@87 516
Chris@87 517 def _g_div_gp(r, n, p, x, y, w):
Chris@87 518 t1 = (r+1)**n
Chris@87 519 t2 = (r+1)**(n-1)
Chris@87 520 return ((y + t1*x + p*(t1 - 1)*(r*w + 1)/r) /
Chris@87 521 (n*t2*x - p*(t1 - 1)*(r*w + 1)/(r**2) + n*p*t2*(r*w + 1)/r +
Chris@87 522 p*(t1 - 1)*w/r))
Chris@87 523
Chris@87 524 # Use Newton's iteration until the change is less than 1e-6
Chris@87 525 # for all values or a maximum of 100 iterations is reached.
Chris@87 526 # Newton's rule is
Chris@87 527 # r_{n+1} = r_{n} - g(r_n)/g'(r_n)
Chris@87 528 # where
Chris@87 529 # g(r) is the formula
Chris@87 530 # g'(r) is the derivative with respect to r.
Chris@87 531 def rate(nper, pmt, pv, fv, when='end', guess=0.10, tol=1e-6, maxiter=100):
Chris@87 532 """
Chris@87 533 Compute the rate of interest per period.
Chris@87 534
Chris@87 535 Parameters
Chris@87 536 ----------
Chris@87 537 nper : array_like
Chris@87 538 Number of compounding periods
Chris@87 539 pmt : array_like
Chris@87 540 Payment
Chris@87 541 pv : array_like
Chris@87 542 Present value
Chris@87 543 fv : array_like
Chris@87 544 Future value
Chris@87 545 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional
Chris@87 546 When payments are due ('begin' (1) or 'end' (0))
Chris@87 547 guess : float, optional
Chris@87 548 Starting guess for solving the rate of interest
Chris@87 549 tol : float, optional
Chris@87 550 Required tolerance for the solution
Chris@87 551 maxiter : int, optional
Chris@87 552 Maximum iterations in finding the solution
Chris@87 553
Chris@87 554 Notes
Chris@87 555 -----
Chris@87 556 The rate of interest is computed by iteratively solving the
Chris@87 557 (non-linear) equation::
Chris@87 558
Chris@87 559 fv + pv*(1+rate)**nper + pmt*(1+rate*when)/rate * ((1+rate)**nper - 1) = 0
Chris@87 560
Chris@87 561 for ``rate``.
Chris@87 562
Chris@87 563 References
Chris@87 564 ----------
Chris@87 565 Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May). Open Document
Chris@87 566 Format for Office Applications (OpenDocument)v1.2, Part 2: Recalculated
Chris@87 567 Formula (OpenFormula) Format - Annotated Version, Pre-Draft 12.
Chris@87 568 Organization for the Advancement of Structured Information Standards
Chris@87 569 (OASIS). Billerica, MA, USA. [ODT Document]. Available:
Chris@87 570 http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula
Chris@87 571 OpenDocument-formula-20090508.odt
Chris@87 572
Chris@87 573 """
Chris@87 574 when = _convert_when(when)
Chris@87 575 (nper, pmt, pv, fv, when) = map(np.asarray, [nper, pmt, pv, fv, when])
Chris@87 576 rn = guess
Chris@87 577 iter = 0
Chris@87 578 close = False
Chris@87 579 while (iter < maxiter) and not close:
Chris@87 580 rnp1 = rn - _g_div_gp(rn, nper, pmt, pv, fv, when)
Chris@87 581 diff = abs(rnp1-rn)
Chris@87 582 close = np.all(diff < tol)
Chris@87 583 iter += 1
Chris@87 584 rn = rnp1
Chris@87 585 if not close:
Chris@87 586 # Return nan's in array of the same shape as rn
Chris@87 587 return np.nan + rn
Chris@87 588 else:
Chris@87 589 return rn
Chris@87 590
Chris@87 591 def irr(values):
Chris@87 592 """
Chris@87 593 Return the Internal Rate of Return (IRR).
Chris@87 594
Chris@87 595 This is the "average" periodically compounded rate of return
Chris@87 596 that gives a net present value of 0.0; for a more complete explanation,
Chris@87 597 see Notes below.
Chris@87 598
Chris@87 599 Parameters
Chris@87 600 ----------
Chris@87 601 values : array_like, shape(N,)
Chris@87 602 Input cash flows per time period. By convention, net "deposits"
Chris@87 603 are negative and net "withdrawals" are positive. Thus, for
Chris@87 604 example, at least the first element of `values`, which represents
Chris@87 605 the initial investment, will typically be negative.
Chris@87 606
Chris@87 607 Returns
Chris@87 608 -------
Chris@87 609 out : float
Chris@87 610 Internal Rate of Return for periodic input values.
Chris@87 611
Chris@87 612 Notes
Chris@87 613 -----
Chris@87 614 The IRR is perhaps best understood through an example (illustrated
Chris@87 615 using np.irr in the Examples section below). Suppose one invests 100
Chris@87 616 units and then makes the following withdrawals at regular (fixed)
Chris@87 617 intervals: 39, 59, 55, 20. Assuming the ending value is 0, one's 100
Chris@87 618 unit investment yields 173 units; however, due to the combination of
Chris@87 619 compounding and the periodic withdrawals, the "average" rate of return
Chris@87 620 is neither simply 0.73/4 nor (1.73)^0.25-1. Rather, it is the solution
Chris@87 621 (for :math:`r`) of the equation:
Chris@87 622
Chris@87 623 .. math:: -100 + \\frac{39}{1+r} + \\frac{59}{(1+r)^2}
Chris@87 624 + \\frac{55}{(1+r)^3} + \\frac{20}{(1+r)^4} = 0
Chris@87 625
Chris@87 626 In general, for `values` :math:`= [v_0, v_1, ... v_M]`,
Chris@87 627 irr is the solution of the equation: [G]_
Chris@87 628
Chris@87 629 .. math:: \\sum_{t=0}^M{\\frac{v_t}{(1+irr)^{t}}} = 0
Chris@87 630
Chris@87 631 References
Chris@87 632 ----------
Chris@87 633 .. [G] L. J. Gitman, "Principles of Managerial Finance, Brief," 3rd ed.,
Chris@87 634 Addison-Wesley, 2003, pg. 348.
Chris@87 635
Chris@87 636 Examples
Chris@87 637 --------
Chris@87 638 >>> round(irr([-100, 39, 59, 55, 20]), 5)
Chris@87 639 0.28095
Chris@87 640 >>> round(irr([-100, 0, 0, 74]), 5)
Chris@87 641 -0.0955
Chris@87 642 >>> round(irr([-100, 100, 0, -7]), 5)
Chris@87 643 -0.0833
Chris@87 644 >>> round(irr([-100, 100, 0, 7]), 5)
Chris@87 645 0.06206
Chris@87 646 >>> round(irr([-5, 10.5, 1, -8, 1]), 5)
Chris@87 647 0.0886
Chris@87 648
Chris@87 649 (Compare with the Example given for numpy.lib.financial.npv)
Chris@87 650
Chris@87 651 """
Chris@87 652 res = np.roots(values[::-1])
Chris@87 653 mask = (res.imag == 0) & (res.real > 0)
Chris@87 654 if res.size == 0:
Chris@87 655 return np.nan
Chris@87 656 res = res[mask].real
Chris@87 657 # NPV(rate) = 0 can have more than one solution so we return
Chris@87 658 # only the solution closest to zero.
Chris@87 659 rate = 1.0/res - 1
Chris@87 660 rate = rate.item(np.argmin(np.abs(rate)))
Chris@87 661 return rate
Chris@87 662
Chris@87 663 def npv(rate, values):
Chris@87 664 """
Chris@87 665 Returns the NPV (Net Present Value) of a cash flow series.
Chris@87 666
Chris@87 667 Parameters
Chris@87 668 ----------
Chris@87 669 rate : scalar
Chris@87 670 The discount rate.
Chris@87 671 values : array_like, shape(M, )
Chris@87 672 The values of the time series of cash flows. The (fixed) time
Chris@87 673 interval between cash flow "events" must be the same as that for
Chris@87 674 which `rate` is given (i.e., if `rate` is per year, then precisely
Chris@87 675 a year is understood to elapse between each cash flow event). By
Chris@87 676 convention, investments or "deposits" are negative, income or
Chris@87 677 "withdrawals" are positive; `values` must begin with the initial
Chris@87 678 investment, thus `values[0]` will typically be negative.
Chris@87 679
Chris@87 680 Returns
Chris@87 681 -------
Chris@87 682 out : float
Chris@87 683 The NPV of the input cash flow series `values` at the discount
Chris@87 684 `rate`.
Chris@87 685
Chris@87 686 Notes
Chris@87 687 -----
Chris@87 688 Returns the result of: [G]_
Chris@87 689
Chris@87 690 .. math :: \\sum_{t=0}^{M-1}{\\frac{values_t}{(1+rate)^{t}}}
Chris@87 691
Chris@87 692 References
Chris@87 693 ----------
Chris@87 694 .. [G] L. J. Gitman, "Principles of Managerial Finance, Brief," 3rd ed.,
Chris@87 695 Addison-Wesley, 2003, pg. 346.
Chris@87 696
Chris@87 697 Examples
Chris@87 698 --------
Chris@87 699 >>> np.npv(0.281,[-100, 39, 59, 55, 20])
Chris@87 700 -0.0084785916384548798
Chris@87 701
Chris@87 702 (Compare with the Example given for numpy.lib.financial.irr)
Chris@87 703
Chris@87 704 """
Chris@87 705 values = np.asarray(values)
Chris@87 706 return (values / (1+rate)**np.arange(0, len(values))).sum(axis=0)
Chris@87 707
Chris@87 708 def mirr(values, finance_rate, reinvest_rate):
Chris@87 709 """
Chris@87 710 Modified internal rate of return.
Chris@87 711
Chris@87 712 Parameters
Chris@87 713 ----------
Chris@87 714 values : array_like
Chris@87 715 Cash flows (must contain at least one positive and one negative
Chris@87 716 value) or nan is returned. The first value is considered a sunk
Chris@87 717 cost at time zero.
Chris@87 718 finance_rate : scalar
Chris@87 719 Interest rate paid on the cash flows
Chris@87 720 reinvest_rate : scalar
Chris@87 721 Interest rate received on the cash flows upon reinvestment
Chris@87 722
Chris@87 723 Returns
Chris@87 724 -------
Chris@87 725 out : float
Chris@87 726 Modified internal rate of return
Chris@87 727
Chris@87 728 """
Chris@87 729 values = np.asarray(values, dtype=np.double)
Chris@87 730 n = values.size
Chris@87 731 pos = values > 0
Chris@87 732 neg = values < 0
Chris@87 733 if not (pos.any() and neg.any()):
Chris@87 734 return np.nan
Chris@87 735 numer = np.abs(npv(reinvest_rate, values*pos))
Chris@87 736 denom = np.abs(npv(finance_rate, values*neg))
Chris@87 737 return (numer/denom)**(1.0/(n - 1))*(1 + reinvest_rate) - 1