Skip to content

Commit c4c7de8

Browse files
committed
Implement Finance::Calculations#irr using Newton's method
1 parent dd1219f commit c4c7de8

File tree

3 files changed

+85
-8
lines changed

3 files changed

+85
-8
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ which are as follows:
99

1010
| numpy-financial function | ruby native function ported? | info|
1111
|:------------------------: |:------------------: | :------------------|
12-
| fv | | Computes the future value|
13-
| ipmt | | Computes interest payment for a loan|
14-
| pmt | | Computes the fixed periodic payment(principal + interest) made against a loan amount|
15-
| ppmt | | Computes principal payment for a loan|
16-
| nper | | Computes the number of periodic payments|
17-
| pv | | Computes the present value of a payment|
18-
| rate | | Computes the rate of interest per period|
12+
| fv | | Computes the future value|
13+
| ipmt | | Computes interest payment for a loan|
14+
| pmt | | Computes the fixed periodic payment(principal + interest) made against a loan amount|
15+
| ppmt | | Computes principal payment for a loan|
16+
| nper | | Computes the number of periodic payments|
17+
| pv | | Computes the present value of a payment|
18+
| rate | | Computes the rate of interest per period|
1919
| irr | | Computes the internal rate of return|
2020
| npv || Computes the net present value of a series of cash flow|
2121
| mirr | | Computes the modified internal rate of return|

lib/finance/calculations.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
# frozen_string_literal: true
22

3+
require 'bigdecimal'
4+
require 'bigdecimal/newton'
5+
6+
include Newton
7+
38
module Finance
49
class Calculations
510
class << self
611
# Npv computes the Net Present Value of a cash flow series.
12+
#
713
# @return [Numeric] The NPV of the input cash flow series `values` at the discount `rate`.
814
#
915
# @param [Numeric] :rate A discount rate applied once per period.
@@ -24,7 +30,58 @@ def npv(rate, values)
2430
npv_value
2531
end
2632

33+
# IRR computes the Rate of Interest per period.
34+
#
35+
# @return [Float] Internal Rate of Return for periodic input values.
36+
#
37+
# @param [Array<Numeric>] :values Input cash flows per time period.
38+
#
39+
# @example
40+
# require 'finance_rb'
41+
# Finance::Calculations.irr([-100, 0, 0, 74]) #=> 0.14299344106053188
42+
#
43+
# @see http://en.wikipedia.org/wiki/Internal_rate_of_return
44+
# @see L. J. Gitman, "Principles of Managerial Finance, Brief," 3rd ed.,
45+
# Addison-Wesley, 2003, pg. 348.
46+
def irr(values)
47+
inflows, outflows = values.partition{ |i| i >= 0 }
48+
if inflows.empty? || outflows.empty?
49+
return 0.0
50+
end
51+
52+
func = BigDecimal.limit(100)
53+
func = Function.new(values)
54+
rate = [ func.one ]
55+
nlsolve(func, rate)
56+
rate[0].to_f
57+
end
58+
2759
alias net_present_value npv
60+
alias internal_return_rate irr
61+
62+
private
63+
64+
# Base class for working with Newton's Method.
65+
# For more details, see Bigdecimal::Newton.
66+
# @api private
67+
class Function
68+
def initialize(values)
69+
@zero = BigDecimal("0.0")
70+
@one = BigDecimal("1.0")
71+
@two = BigDecimal("2.0")
72+
@ten = BigDecimal("10.0")
73+
@eps = BigDecimal("1.0e-16")
74+
@values = values
75+
end
76+
77+
def zero; @zero; end
78+
def one ; @one; end
79+
def two ; @two; end
80+
def ten ; @ten; end
81+
def eps ; @eps; end
82+
83+
def values(x); [Finance::Calculations.npv(x[0], @values)]; end
84+
end
2885
end
2986
end
3087
end

spec/finance/calculations_spec.rb

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,30 @@
1212
).to eq(-85.07888805409468)
1313
end
1414

15-
it 'returns correct npv for zero rates' do
15+
it 'calculates correct npv for zero rates' do
1616
expect(
1717
Finance::Calculations.net_present_value(0.0, [-2000, 55, 55, 55])
1818
).to eq(-1835.0)
1919
end
2020
end
21+
22+
describe '#irr' do
23+
it 'calculates correct irr value' do
24+
expect(
25+
Finance::Calculations.irr([-4000,1200,1410,1875,1050])
26+
).to eq(0.14299344106053188)
27+
end
28+
29+
it 'calculates zero value for cashflows w/o any inflows' do
30+
expect(
31+
Finance::Calculations.irr([100,500,200,50])
32+
).to eq(0.0)
33+
end
34+
35+
it 'is available through alias name' do
36+
expect(
37+
Finance::Calculations.internal_return_rate([-100, 0, 0, 74])
38+
).to eq(-0.09549583035161031)
39+
end
40+
end
2141
end

0 commit comments

Comments
 (0)