Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""Some simple financial calculations 

2 

3patterned after spreadsheet computations. 

4 

5There is some complexity in each function 

6so that the functions behave like ufuncs with 

7broadcasting and being able to be called with scalars 

8or arrays (or other sequences). 

9 

10Functions support the :class:`decimal.Decimal` type unless 

11otherwise stated. 

12""" 

13import warnings 

14from decimal import Decimal 

15import functools 

16 

17import numpy as np 

18from numpy.core import overrides 

19 

20 

21_depmsg = ("numpy.{name} is deprecated and will be removed from NumPy 1.20. " 

22 "Use numpy_financial.{name} instead " 

23 "(https://pypi.org/project/numpy-financial/).") 

24 

25array_function_dispatch = functools.partial( 

26 overrides.array_function_dispatch, module='numpy') 

27 

28 

29__all__ = ['fv', 'pmt', 'nper', 'ipmt', 'ppmt', 'pv', 'rate', 

30 'irr', 'npv', 'mirr'] 

31 

32_when_to_num = {'end':0, 'begin':1, 

33 'e':0, 'b':1, 

34 0:0, 1:1, 

35 'beginning':1, 

36 'start':1, 

37 'finish':0} 

38 

39def _convert_when(when): 

40 #Test to see if when has already been converted to ndarray 

41 #This will happen if one function calls another, for example ppmt 

42 if isinstance(when, np.ndarray): 

43 return when 

44 try: 

45 return _when_to_num[when] 

46 except (KeyError, TypeError): 

47 return [_when_to_num[x] for x in when] 

48 

49 

50def _fv_dispatcher(rate, nper, pmt, pv, when=None): 

51 warnings.warn(_depmsg.format(name='fv'), 

52 DeprecationWarning, stacklevel=3) 

53 return (rate, nper, pmt, pv) 

54 

55 

56@array_function_dispatch(_fv_dispatcher) 

57def fv(rate, nper, pmt, pv, when='end'): 

58 """ 

59 Compute the future value. 

60 

61 .. deprecated:: 1.18 

62 

63 `fv` is deprecated; for details, see NEP 32 [1]_. 

64 Use the corresponding function in the numpy-financial library, 

65 https://pypi.org/project/numpy-financial. 

66 

67 Given: 

68 * a present value, `pv` 

69 * an interest `rate` compounded once per period, of which 

70 there are 

71 * `nper` total 

72 * a (fixed) payment, `pmt`, paid either 

73 * at the beginning (`when` = {'begin', 1}) or the end 

74 (`when` = {'end', 0}) of each period 

75 

76 Return: 

77 the value at the end of the `nper` periods 

78 

79 Parameters 

80 ---------- 

81 rate : scalar or array_like of shape(M, ) 

82 Rate of interest as decimal (not per cent) per period 

83 nper : scalar or array_like of shape(M, ) 

84 Number of compounding periods 

85 pmt : scalar or array_like of shape(M, ) 

86 Payment 

87 pv : scalar or array_like of shape(M, ) 

88 Present value 

89 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional 

90 When payments are due ('begin' (1) or 'end' (0)). 

91 Defaults to {'end', 0}. 

92 

93 Returns 

94 ------- 

95 out : ndarray 

96 Future values. If all input is scalar, returns a scalar float. If 

97 any input is array_like, returns future values for each input element. 

98 If multiple inputs are array_like, they all must have the same shape. 

99 

100 Notes 

101 ----- 

102 The future value is computed by solving the equation:: 

103 

104 fv + 

105 pv*(1+rate)**nper + 

106 pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) == 0 

107 

108 or, when ``rate == 0``:: 

109 

110 fv + pv + pmt * nper == 0 

111 

112 References 

113 ---------- 

114 .. [1] NumPy Enhancement Proposal (NEP) 32, 

115 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

116 .. [2] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May). 

117 Open Document Format for Office Applications (OpenDocument)v1.2, 

118 Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version, 

119 Pre-Draft 12. Organization for the Advancement of Structured Information 

120 Standards (OASIS). Billerica, MA, USA. [ODT Document]. 

121 Available: 

122 http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula 

123 OpenDocument-formula-20090508.odt 

124 

125 

126 Examples 

127 -------- 

128 What is the future value after 10 years of saving $100 now, with 

129 an additional monthly savings of $100. Assume the interest rate is 

130 5% (annually) compounded monthly? 

131 

132 >>> np.fv(0.05/12, 10*12, -100, -100) 

133 15692.928894335748 

134 

135 By convention, the negative sign represents cash flow out (i.e. money not 

136 available today). Thus, saving $100 a month at 5% annual interest leads 

137 to $15,692.93 available to spend in 10 years. 

138 

139 If any input is array_like, returns an array of equal shape. Let's 

140 compare different interest rates from the example above. 

141 

142 >>> a = np.array((0.05, 0.06, 0.07))/12 

143 >>> np.fv(a, 10*12, -100, -100) 

144 array([ 15692.92889434, 16569.87435405, 17509.44688102]) # may vary 

145 

146 """ 

147 when = _convert_when(when) 

148 (rate, nper, pmt, pv, when) = map(np.asarray, [rate, nper, pmt, pv, when]) 

149 temp = (1+rate)**nper 

150 fact = np.where(rate == 0, nper, 

151 (1 + rate*when)*(temp - 1)/rate) 

152 return -(pv*temp + pmt*fact) 

153 

154 

155def _pmt_dispatcher(rate, nper, pv, fv=None, when=None): 

156 warnings.warn(_depmsg.format(name='pmt'), 

157 DeprecationWarning, stacklevel=3) 

158 return (rate, nper, pv, fv) 

159 

160 

161@array_function_dispatch(_pmt_dispatcher) 

162def pmt(rate, nper, pv, fv=0, when='end'): 

163 """ 

164 Compute the payment against loan principal plus interest. 

165 

166 .. deprecated:: 1.18 

167 

168 `pmt` is deprecated; for details, see NEP 32 [1]_. 

169 Use the corresponding function in the numpy-financial library, 

170 https://pypi.org/project/numpy-financial. 

171 

172 Given: 

173 * a present value, `pv` (e.g., an amount borrowed) 

174 * a future value, `fv` (e.g., 0) 

175 * an interest `rate` compounded once per period, of which 

176 there are 

177 * `nper` total 

178 * and (optional) specification of whether payment is made 

179 at the beginning (`when` = {'begin', 1}) or the end 

180 (`when` = {'end', 0}) of each period 

181 

182 Return: 

183 the (fixed) periodic payment. 

184 

185 Parameters 

186 ---------- 

187 rate : array_like 

188 Rate of interest (per period) 

189 nper : array_like 

190 Number of compounding periods 

191 pv : array_like 

192 Present value 

193 fv : array_like, optional 

194 Future value (default = 0) 

195 when : {{'begin', 1}, {'end', 0}}, {string, int} 

196 When payments are due ('begin' (1) or 'end' (0)) 

197 

198 Returns 

199 ------- 

200 out : ndarray 

201 Payment against loan plus interest. If all input is scalar, returns a 

202 scalar float. If any input is array_like, returns payment for each 

203 input element. If multiple inputs are array_like, they all must have 

204 the same shape. 

205 

206 Notes 

207 ----- 

208 The payment is computed by solving the equation:: 

209 

210 fv + 

211 pv*(1 + rate)**nper + 

212 pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) == 0 

213 

214 or, when ``rate == 0``:: 

215 

216 fv + pv + pmt * nper == 0 

217 

218 for ``pmt``. 

219 

220 Note that computing a monthly mortgage payment is only 

221 one use for this function. For example, pmt returns the 

222 periodic deposit one must make to achieve a specified 

223 future balance given an initial deposit, a fixed, 

224 periodically compounded interest rate, and the total 

225 number of periods. 

226 

227 References 

228 ---------- 

229 .. [1] NumPy Enhancement Proposal (NEP) 32, 

230 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

231 .. [2] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May). 

232 Open Document Format for Office Applications (OpenDocument)v1.2, 

233 Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version, 

234 Pre-Draft 12. Organization for the Advancement of Structured Information 

235 Standards (OASIS). Billerica, MA, USA. [ODT Document]. 

236 Available: 

237 http://www.oasis-open.org/committees/documents.php 

238 ?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt 

239 

240 Examples 

241 -------- 

242 What is the monthly payment needed to pay off a $200,000 loan in 15 

243 years at an annual interest rate of 7.5%? 

244 

245 >>> np.pmt(0.075/12, 12*15, 200000) 

246 -1854.0247200054619 

247 

248 In order to pay-off (i.e., have a future-value of 0) the $200,000 obtained 

249 today, a monthly payment of $1,854.02 would be required. Note that this 

250 example illustrates usage of `fv` having a default value of 0. 

251 

252 """ 

253 when = _convert_when(when) 

254 (rate, nper, pv, fv, when) = map(np.array, [rate, nper, pv, fv, when]) 

255 temp = (1 + rate)**nper 

256 mask = (rate == 0) 

257 masked_rate = np.where(mask, 1, rate) 

258 fact = np.where(mask != 0, nper, 

259 (1 + masked_rate*when)*(temp - 1)/masked_rate) 

260 return -(fv + pv*temp) / fact 

261 

262 

263def _nper_dispatcher(rate, pmt, pv, fv=None, when=None): 

264 warnings.warn(_depmsg.format(name='nper'), 

265 DeprecationWarning, stacklevel=3) 

266 return (rate, pmt, pv, fv) 

267 

268 

269@array_function_dispatch(_nper_dispatcher) 

270def nper(rate, pmt, pv, fv=0, when='end'): 

271 """ 

272 Compute the number of periodic payments. 

273 

274 .. deprecated:: 1.18 

275 

276 `nper` is deprecated; for details, see NEP 32 [1]_. 

277 Use the corresponding function in the numpy-financial library, 

278 https://pypi.org/project/numpy-financial. 

279 

280 :class:`decimal.Decimal` type is not supported. 

281 

282 Parameters 

283 ---------- 

284 rate : array_like 

285 Rate of interest (per period) 

286 pmt : array_like 

287 Payment 

288 pv : array_like 

289 Present value 

290 fv : array_like, optional 

291 Future value 

292 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional 

293 When payments are due ('begin' (1) or 'end' (0)) 

294 

295 Notes 

296 ----- 

297 The number of periods ``nper`` is computed by solving the equation:: 

298 

299 fv + pv*(1+rate)**nper + pmt*(1+rate*when)/rate*((1+rate)**nper-1) = 0 

300 

301 but if ``rate = 0`` then:: 

302 

303 fv + pv + pmt*nper = 0 

304 

305 References 

306 ---------- 

307 .. [1] NumPy Enhancement Proposal (NEP) 32, 

308 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

309 

310 Examples 

311 -------- 

312 If you only had $150/month to pay towards the loan, how long would it take 

313 to pay-off a loan of $8,000 at 7% annual interest? 

314 

315 >>> print(np.round(np.nper(0.07/12, -150, 8000), 5)) 

316 64.07335 

317 

318 So, over 64 months would be required to pay off the loan. 

319 

320 The same analysis could be done with several different interest rates 

321 and/or payments and/or total amounts to produce an entire table. 

322 

323 >>> np.nper(*(np.ogrid[0.07/12: 0.08/12: 0.01/12, 

324 ... -150 : -99 : 50 , 

325 ... 8000 : 9001 : 1000])) 

326 array([[[ 64.07334877, 74.06368256], 

327 [108.07548412, 127.99022654]], 

328 [[ 66.12443902, 76.87897353], 

329 [114.70165583, 137.90124779]]]) 

330 

331 """ 

332 when = _convert_when(when) 

333 (rate, pmt, pv, fv, when) = map(np.asarray, [rate, pmt, pv, fv, when]) 

334 

335 use_zero_rate = False 

336 with np.errstate(divide="raise"): 

337 try: 

338 z = pmt*(1+rate*when)/rate 

339 except FloatingPointError: 

340 use_zero_rate = True 

341 

342 if use_zero_rate: 

343 return (-fv + pv) / pmt 

344 else: 

345 A = -(fv + pv)/(pmt+0) 

346 B = np.log((-fv+z) / (pv+z))/np.log(1+rate) 

347 return np.where(rate == 0, A, B) 

348 

349 

350def _ipmt_dispatcher(rate, per, nper, pv, fv=None, when=None): 

351 warnings.warn(_depmsg.format(name='ipmt'), 

352 DeprecationWarning, stacklevel=3) 

353 return (rate, per, nper, pv, fv) 

354 

355 

356@array_function_dispatch(_ipmt_dispatcher) 

357def ipmt(rate, per, nper, pv, fv=0, when='end'): 

358 """ 

359 Compute the interest portion of a payment. 

360 

361 .. deprecated:: 1.18 

362 

363 `ipmt` is deprecated; for details, see NEP 32 [1]_. 

364 Use the corresponding function in the numpy-financial library, 

365 https://pypi.org/project/numpy-financial. 

366 

367 Parameters 

368 ---------- 

369 rate : scalar or array_like of shape(M, ) 

370 Rate of interest as decimal (not per cent) per period 

371 per : scalar or array_like of shape(M, ) 

372 Interest paid against the loan changes during the life or the loan. 

373 The `per` is the payment period to calculate the interest amount. 

374 nper : scalar or array_like of shape(M, ) 

375 Number of compounding periods 

376 pv : scalar or array_like of shape(M, ) 

377 Present value 

378 fv : scalar or array_like of shape(M, ), optional 

379 Future value 

380 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional 

381 When payments are due ('begin' (1) or 'end' (0)). 

382 Defaults to {'end', 0}. 

383 

384 Returns 

385 ------- 

386 out : ndarray 

387 Interest portion of payment. If all input is scalar, returns a scalar 

388 float. If any input is array_like, returns interest payment for each 

389 input element. If multiple inputs are array_like, they all must have 

390 the same shape. 

391 

392 See Also 

393 -------- 

394 ppmt, pmt, pv 

395 

396 Notes 

397 ----- 

398 The total payment is made up of payment against principal plus interest. 

399 

400 ``pmt = ppmt + ipmt`` 

401 

402 References 

403 ---------- 

404 .. [1] NumPy Enhancement Proposal (NEP) 32, 

405 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

406 

407 Examples 

408 -------- 

409 What is the amortization schedule for a 1 year loan of $2500 at 

410 8.24% interest per year compounded monthly? 

411 

412 >>> principal = 2500.00 

413 

414 The 'per' variable represents the periods of the loan. Remember that 

415 financial equations start the period count at 1! 

416 

417 >>> per = np.arange(1*12) + 1 

418 >>> ipmt = np.ipmt(0.0824/12, per, 1*12, principal) 

419 >>> ppmt = np.ppmt(0.0824/12, per, 1*12, principal) 

420 

421 Each element of the sum of the 'ipmt' and 'ppmt' arrays should equal 

422 'pmt'. 

423 

424 >>> pmt = np.pmt(0.0824/12, 1*12, principal) 

425 >>> np.allclose(ipmt + ppmt, pmt) 

426 True 

427 

428 >>> fmt = '{0:2d} {1:8.2f} {2:8.2f} {3:8.2f}' 

429 >>> for payment in per: 

430 ... index = payment - 1 

431 ... principal = principal + ppmt[index] 

432 ... print(fmt.format(payment, ppmt[index], ipmt[index], principal)) 

433 1 -200.58 -17.17 2299.42 

434 2 -201.96 -15.79 2097.46 

435 3 -203.35 -14.40 1894.11 

436 4 -204.74 -13.01 1689.37 

437 5 -206.15 -11.60 1483.22 

438 6 -207.56 -10.18 1275.66 

439 7 -208.99 -8.76 1066.67 

440 8 -210.42 -7.32 856.25 

441 9 -211.87 -5.88 644.38 

442 10 -213.32 -4.42 431.05 

443 11 -214.79 -2.96 216.26 

444 12 -216.26 -1.49 -0.00 

445 

446 >>> interestpd = np.sum(ipmt) 

447 >>> np.round(interestpd, 2) 

448 -112.98 

449 

450 """ 

451 when = _convert_when(when) 

452 rate, per, nper, pv, fv, when = np.broadcast_arrays(rate, per, nper, 

453 pv, fv, when) 

454 total_pmt = pmt(rate, nper, pv, fv, when) 

455 ipmt = _rbl(rate, per, total_pmt, pv, when)*rate 

456 try: 

457 ipmt = np.where(when == 1, ipmt/(1 + rate), ipmt) 

458 ipmt = np.where(np.logical_and(when == 1, per == 1), 0, ipmt) 

459 except IndexError: 

460 pass 

461 return ipmt 

462 

463 

464def _rbl(rate, per, pmt, pv, when): 

465 """ 

466 This function is here to simply have a different name for the 'fv' 

467 function to not interfere with the 'fv' keyword argument within the 'ipmt' 

468 function. It is the 'remaining balance on loan' which might be useful as 

469 its own function, but is easily calculated with the 'fv' function. 

470 """ 

471 return fv(rate, (per - 1), pmt, pv, when) 

472 

473 

474def _ppmt_dispatcher(rate, per, nper, pv, fv=None, when=None): 

475 warnings.warn(_depmsg.format(name='ppmt'), 

476 DeprecationWarning, stacklevel=3) 

477 return (rate, per, nper, pv, fv) 

478 

479 

480@array_function_dispatch(_ppmt_dispatcher) 

481def ppmt(rate, per, nper, pv, fv=0, when='end'): 

482 """ 

483 Compute the payment against loan principal. 

484 

485 .. deprecated:: 1.18 

486 

487 `ppmt` is deprecated; for details, see NEP 32 [1]_. 

488 Use the corresponding function in the numpy-financial library, 

489 https://pypi.org/project/numpy-financial. 

490 

491 Parameters 

492 ---------- 

493 rate : array_like 

494 Rate of interest (per period) 

495 per : array_like, int 

496 Amount paid against the loan changes. The `per` is the period of 

497 interest. 

498 nper : array_like 

499 Number of compounding periods 

500 pv : array_like 

501 Present value 

502 fv : array_like, optional 

503 Future value 

504 when : {{'begin', 1}, {'end', 0}}, {string, int} 

505 When payments are due ('begin' (1) or 'end' (0)) 

506 

507 See Also 

508 -------- 

509 pmt, pv, ipmt 

510 

511 References 

512 ---------- 

513 .. [1] NumPy Enhancement Proposal (NEP) 32, 

514 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

515 

516 """ 

517 total = pmt(rate, nper, pv, fv, when) 

518 return total - ipmt(rate, per, nper, pv, fv, when) 

519 

520 

521def _pv_dispatcher(rate, nper, pmt, fv=None, when=None): 

522 warnings.warn(_depmsg.format(name='pv'), 

523 DeprecationWarning, stacklevel=3) 

524 return (rate, nper, nper, pv, fv) 

525 

526 

527@array_function_dispatch(_pv_dispatcher) 

528def pv(rate, nper, pmt, fv=0, when='end'): 

529 """ 

530 Compute the present value. 

531 

532 .. deprecated:: 1.18 

533 

534 `pv` is deprecated; for details, see NEP 32 [1]_. 

535 Use the corresponding function in the numpy-financial library, 

536 https://pypi.org/project/numpy-financial. 

537 

538 Given: 

539 * a future value, `fv` 

540 * an interest `rate` compounded once per period, of which 

541 there are 

542 * `nper` total 

543 * a (fixed) payment, `pmt`, paid either 

544 * at the beginning (`when` = {'begin', 1}) or the end 

545 (`when` = {'end', 0}) of each period 

546 

547 Return: 

548 the value now 

549 

550 Parameters 

551 ---------- 

552 rate : array_like 

553 Rate of interest (per period) 

554 nper : array_like 

555 Number of compounding periods 

556 pmt : array_like 

557 Payment 

558 fv : array_like, optional 

559 Future value 

560 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional 

561 When payments are due ('begin' (1) or 'end' (0)) 

562 

563 Returns 

564 ------- 

565 out : ndarray, float 

566 Present value of a series of payments or investments. 

567 

568 Notes 

569 ----- 

570 The present value is computed by solving the equation:: 

571 

572 fv + 

573 pv*(1 + rate)**nper + 

574 pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) = 0 

575 

576 or, when ``rate = 0``:: 

577 

578 fv + pv + pmt * nper = 0 

579 

580 for `pv`, which is then returned. 

581 

582 References 

583 ---------- 

584 .. [1] NumPy Enhancement Proposal (NEP) 32, 

585 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

586 .. [2] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May). 

587 Open Document Format for Office Applications (OpenDocument)v1.2, 

588 Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version, 

589 Pre-Draft 12. Organization for the Advancement of Structured Information 

590 Standards (OASIS). Billerica, MA, USA. [ODT Document]. 

591 Available: 

592 http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula 

593 OpenDocument-formula-20090508.odt 

594 

595 Examples 

596 -------- 

597 What is the present value (e.g., the initial investment) 

598 of an investment that needs to total $15692.93 

599 after 10 years of saving $100 every month? Assume the 

600 interest rate is 5% (annually) compounded monthly. 

601 

602 >>> np.pv(0.05/12, 10*12, -100, 15692.93) 

603 -100.00067131625819 

604 

605 By convention, the negative sign represents cash flow out 

606 (i.e., money not available today). Thus, to end up with 

607 $15,692.93 in 10 years saving $100 a month at 5% annual 

608 interest, one's initial deposit should also be $100. 

609 

610 If any input is array_like, ``pv`` returns an array of equal shape. 

611 Let's compare different interest rates in the example above: 

612 

613 >>> a = np.array((0.05, 0.04, 0.03))/12 

614 >>> np.pv(a, 10*12, -100, 15692.93) 

615 array([ -100.00067132, -649.26771385, -1273.78633713]) # may vary 

616 

617 So, to end up with the same $15692.93 under the same $100 per month 

618 "savings plan," for annual interest rates of 4% and 3%, one would 

619 need initial investments of $649.27 and $1273.79, respectively. 

620 

621 """ 

622 when = _convert_when(when) 

623 (rate, nper, pmt, fv, when) = map(np.asarray, [rate, nper, pmt, fv, when]) 

624 temp = (1+rate)**nper 

625 fact = np.where(rate == 0, nper, (1+rate*when)*(temp-1)/rate) 

626 return -(fv + pmt*fact)/temp 

627 

628# Computed with Sage 

629# (y + (r + 1)^n*x + p*((r + 1)^n - 1)*(r*w + 1)/r)/(n*(r + 1)^(n - 1)*x - 

630# p*((r + 1)^n - 1)*(r*w + 1)/r^2 + n*p*(r + 1)^(n - 1)*(r*w + 1)/r + 

631# p*((r + 1)^n - 1)*w/r) 

632 

633def _g_div_gp(r, n, p, x, y, w): 

634 t1 = (r+1)**n 

635 t2 = (r+1)**(n-1) 

636 return ((y + t1*x + p*(t1 - 1)*(r*w + 1)/r) / 

637 (n*t2*x - p*(t1 - 1)*(r*w + 1)/(r**2) + n*p*t2*(r*w + 1)/r + 

638 p*(t1 - 1)*w/r)) 

639 

640 

641def _rate_dispatcher(nper, pmt, pv, fv, when=None, guess=None, tol=None, 

642 maxiter=None): 

643 warnings.warn(_depmsg.format(name='rate'), 

644 DeprecationWarning, stacklevel=3) 

645 return (nper, pmt, pv, fv) 

646 

647 

648# Use Newton's iteration until the change is less than 1e-6 

649# for all values or a maximum of 100 iterations is reached. 

650# Newton's rule is 

651# r_{n+1} = r_{n} - g(r_n)/g'(r_n) 

652# where 

653# g(r) is the formula 

654# g'(r) is the derivative with respect to r. 

655@array_function_dispatch(_rate_dispatcher) 

656def rate(nper, pmt, pv, fv, when='end', guess=None, tol=None, maxiter=100): 

657 """ 

658 Compute the rate of interest per period. 

659 

660 .. deprecated:: 1.18 

661 

662 `rate` is deprecated; for details, see NEP 32 [1]_. 

663 Use the corresponding function in the numpy-financial library, 

664 https://pypi.org/project/numpy-financial. 

665 

666 Parameters 

667 ---------- 

668 nper : array_like 

669 Number of compounding periods 

670 pmt : array_like 

671 Payment 

672 pv : array_like 

673 Present value 

674 fv : array_like 

675 Future value 

676 when : {{'begin', 1}, {'end', 0}}, {string, int}, optional 

677 When payments are due ('begin' (1) or 'end' (0)) 

678 guess : Number, optional 

679 Starting guess for solving the rate of interest, default 0.1 

680 tol : Number, optional 

681 Required tolerance for the solution, default 1e-6 

682 maxiter : int, optional 

683 Maximum iterations in finding the solution 

684 

685 Notes 

686 ----- 

687 The rate of interest is computed by iteratively solving the 

688 (non-linear) equation:: 

689 

690 fv + pv*(1+rate)**nper + pmt*(1+rate*when)/rate * ((1+rate)**nper - 1) = 0 

691 

692 for ``rate``. 

693 

694 References 

695 ---------- 

696 .. [1] NumPy Enhancement Proposal (NEP) 32, 

697 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

698 .. [2] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May). 

699 Open Document Format for Office Applications (OpenDocument)v1.2, 

700 Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version, 

701 Pre-Draft 12. Organization for the Advancement of Structured Information 

702 Standards (OASIS). Billerica, MA, USA. [ODT Document]. 

703 Available: 

704 http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula 

705 OpenDocument-formula-20090508.odt 

706 

707 """ 

708 when = _convert_when(when) 

709 default_type = Decimal if isinstance(pmt, Decimal) else float 

710 

711 # Handle casting defaults to Decimal if/when pmt is a Decimal and 

712 # guess and/or tol are not given default values 

713 if guess is None: 

714 guess = default_type('0.1') 

715 

716 if tol is None: 

717 tol = default_type('1e-6') 

718 

719 (nper, pmt, pv, fv, when) = map(np.asarray, [nper, pmt, pv, fv, when]) 

720 

721 rn = guess 

722 iterator = 0 

723 close = False 

724 while (iterator < maxiter) and not close: 

725 rnp1 = rn - _g_div_gp(rn, nper, pmt, pv, fv, when) 

726 diff = abs(rnp1-rn) 

727 close = np.all(diff < tol) 

728 iterator += 1 

729 rn = rnp1 

730 if not close: 

731 # Return nan's in array of the same shape as rn 

732 return np.nan + rn 

733 else: 

734 return rn 

735 

736 

737def _irr_dispatcher(values): 

738 warnings.warn(_depmsg.format(name='irr'), 

739 DeprecationWarning, stacklevel=3) 

740 return (values,) 

741 

742 

743@array_function_dispatch(_irr_dispatcher) 

744def irr(values): 

745 """ 

746 Return the Internal Rate of Return (IRR). 

747 

748 .. deprecated:: 1.18 

749 

750 `irr` is deprecated; for details, see NEP 32 [1]_. 

751 Use the corresponding function in the numpy-financial library, 

752 https://pypi.org/project/numpy-financial. 

753 

754 This is the "average" periodically compounded rate of return 

755 that gives a net present value of 0.0; for a more complete explanation, 

756 see Notes below. 

757 

758 :class:`decimal.Decimal` type is not supported. 

759 

760 Parameters 

761 ---------- 

762 values : array_like, shape(N,) 

763 Input cash flows per time period. By convention, net "deposits" 

764 are negative and net "withdrawals" are positive. Thus, for 

765 example, at least the first element of `values`, which represents 

766 the initial investment, will typically be negative. 

767 

768 Returns 

769 ------- 

770 out : float 

771 Internal Rate of Return for periodic input values. 

772 

773 Notes 

774 ----- 

775 The IRR is perhaps best understood through an example (illustrated 

776 using np.irr in the Examples section below). Suppose one invests 100 

777 units and then makes the following withdrawals at regular (fixed) 

778 intervals: 39, 59, 55, 20. Assuming the ending value is 0, one's 100 

779 unit investment yields 173 units; however, due to the combination of 

780 compounding and the periodic withdrawals, the "average" rate of return 

781 is neither simply 0.73/4 nor (1.73)^0.25-1. Rather, it is the solution 

782 (for :math:`r`) of the equation: 

783 

784 .. math:: -100 + \\frac{39}{1+r} + \\frac{59}{(1+r)^2} 

785 + \\frac{55}{(1+r)^3} + \\frac{20}{(1+r)^4} = 0 

786 

787 In general, for `values` :math:`= [v_0, v_1, ... v_M]`, 

788 irr is the solution of the equation: [2]_ 

789 

790 .. math:: \\sum_{t=0}^M{\\frac{v_t}{(1+irr)^{t}}} = 0 

791 

792 References 

793 ---------- 

794 .. [1] NumPy Enhancement Proposal (NEP) 32, 

795 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

796 .. [2] L. J. Gitman, "Principles of Managerial Finance, Brief," 3rd ed., 

797 Addison-Wesley, 2003, pg. 348. 

798 

799 Examples 

800 -------- 

801 >>> round(np.irr([-100, 39, 59, 55, 20]), 5) 

802 0.28095 

803 >>> round(np.irr([-100, 0, 0, 74]), 5) 

804 -0.0955 

805 >>> round(np.irr([-100, 100, 0, -7]), 5) 

806 -0.0833 

807 >>> round(np.irr([-100, 100, 0, 7]), 5) 

808 0.06206 

809 >>> round(np.irr([-5, 10.5, 1, -8, 1]), 5) 

810 0.0886 

811 

812 """ 

813 # `np.roots` call is why this function does not support Decimal type. 

814 # 

815 # Ultimately Decimal support needs to be added to np.roots, which has 

816 # greater implications on the entire linear algebra module and how it does 

817 # eigenvalue computations. 

818 res = np.roots(values[::-1]) 

819 mask = (res.imag == 0) & (res.real > 0) 

820 if not mask.any(): 

821 return np.nan 

822 res = res[mask].real 

823 # NPV(rate) = 0 can have more than one solution so we return 

824 # only the solution closest to zero. 

825 rate = 1/res - 1 

826 rate = rate.item(np.argmin(np.abs(rate))) 

827 return rate 

828 

829 

830def _npv_dispatcher(rate, values): 

831 warnings.warn(_depmsg.format(name='npv'), 

832 DeprecationWarning, stacklevel=3) 

833 return (values,) 

834 

835 

836@array_function_dispatch(_npv_dispatcher) 

837def npv(rate, values): 

838 """ 

839 Returns the NPV (Net Present Value) of a cash flow series. 

840 

841 .. deprecated:: 1.18 

842 

843 `npv` is deprecated; for details, see NEP 32 [1]_. 

844 Use the corresponding function in the numpy-financial library, 

845 https://pypi.org/project/numpy-financial. 

846 

847 Parameters 

848 ---------- 

849 rate : scalar 

850 The discount rate. 

851 values : array_like, shape(M, ) 

852 The values of the time series of cash flows. The (fixed) time 

853 interval between cash flow "events" must be the same as that for 

854 which `rate` is given (i.e., if `rate` is per year, then precisely 

855 a year is understood to elapse between each cash flow event). By 

856 convention, investments or "deposits" are negative, income or 

857 "withdrawals" are positive; `values` must begin with the initial 

858 investment, thus `values[0]` will typically be negative. 

859 

860 Returns 

861 ------- 

862 out : float 

863 The NPV of the input cash flow series `values` at the discount 

864 `rate`. 

865 

866 Warnings 

867 -------- 

868 ``npv`` considers a series of cashflows starting in the present (t = 0). 

869 NPV can also be defined with a series of future cashflows, paid at the 

870 end, rather than the start, of each period. If future cashflows are used, 

871 the first cashflow `values[0]` must be zeroed and added to the net 

872 present value of the future cashflows. This is demonstrated in the 

873 examples. 

874 

875 Notes 

876 ----- 

877 Returns the result of: [2]_ 

878 

879 .. math :: \\sum_{t=0}^{M-1}{\\frac{values_t}{(1+rate)^{t}}} 

880 

881 References 

882 ---------- 

883 .. [1] NumPy Enhancement Proposal (NEP) 32, 

884 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

885 .. [2] L. J. Gitman, "Principles of Managerial Finance, Brief," 3rd ed., 

886 Addison-Wesley, 2003, pg. 346. 

887 

888 Examples 

889 -------- 

890 Consider a potential project with an initial investment of $40 000 and 

891 projected cashflows of $5 000, $8 000, $12 000 and $30 000 at the end of 

892 each period discounted at a rate of 8% per period. To find the project's 

893 net present value: 

894 

895 >>> rate, cashflows = 0.08, [-40_000, 5_000, 8_000, 12_000, 30_000] 

896 >>> np.npv(rate, cashflows).round(5) 

897 3065.22267 

898 

899 It may be preferable to split the projected cashflow into an initial 

900 investment and expected future cashflows. In this case, the value of 

901 the initial cashflow is zero and the initial investment is later added 

902 to the future cashflows net present value: 

903 

904 >>> initial_cashflow = cashflows[0] 

905 >>> cashflows[0] = 0 

906 >>> np.round(np.npv(rate, cashflows) + initial_cashflow, 5) 

907 3065.22267 

908 

909 """ 

910 values = np.asarray(values) 

911 return (values / (1+rate)**np.arange(0, len(values))).sum(axis=0) 

912 

913 

914def _mirr_dispatcher(values, finance_rate, reinvest_rate): 

915 warnings.warn(_depmsg.format(name='mirr'), 

916 DeprecationWarning, stacklevel=3) 

917 return (values,) 

918 

919 

920@array_function_dispatch(_mirr_dispatcher) 

921def mirr(values, finance_rate, reinvest_rate): 

922 """ 

923 Modified internal rate of return. 

924 

925 .. deprecated:: 1.18 

926 

927 `mirr` is deprecated; for details, see NEP 32 [1]_. 

928 Use the corresponding function in the numpy-financial library, 

929 https://pypi.org/project/numpy-financial. 

930 

931 Parameters 

932 ---------- 

933 values : array_like 

934 Cash flows (must contain at least one positive and one negative 

935 value) or nan is returned. The first value is considered a sunk 

936 cost at time zero. 

937 finance_rate : scalar 

938 Interest rate paid on the cash flows 

939 reinvest_rate : scalar 

940 Interest rate received on the cash flows upon reinvestment 

941 

942 Returns 

943 ------- 

944 out : float 

945 Modified internal rate of return 

946 

947 References 

948 ---------- 

949 .. [1] NumPy Enhancement Proposal (NEP) 32, 

950 https://numpy.org/neps/nep-0032-remove-financial-functions.html 

951 """ 

952 values = np.asarray(values) 

953 n = values.size 

954 

955 # Without this explicit cast the 1/(n - 1) computation below 

956 # becomes a float, which causes TypeError when using Decimal 

957 # values. 

958 if isinstance(finance_rate, Decimal): 

959 n = Decimal(n) 

960 

961 pos = values > 0 

962 neg = values < 0 

963 if not (pos.any() and neg.any()): 

964 return np.nan 

965 numer = np.abs(npv(reinvest_rate, values*pos)) 

966 denom = np.abs(npv(finance_rate, values*neg)) 

967 return (numer/denom)**(1/(n - 1))*(1 + reinvest_rate) - 1