finance 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY +7 -0
- data/lib/finance/amortization.rb +1 -1
- data/lib/finance/cashflows.rb +72 -12
- data/lib/finance/decimal.rb +11 -2
- data/lib/finance/gistfile1.rb +25 -0
- data/lib/finance/transaction.rb +3 -0
- data/test/test_cashflows.rb +18 -1
- metadata +5 -4
data/HISTORY
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
= Version 1.1.0
|
2
|
+
11 Sep 2011
|
3
|
+
|
4
|
+
* Added XNPV and XIRR functions, with basic testing.
|
5
|
+
* Bugfix: Array#sum no longer collides with the Array#sum defined in Rails.
|
6
|
+
* Bugfix: Numeric#amortize now correctly calls Finance::Amortization#new.
|
7
|
+
|
1
8
|
= Version 1.0.0
|
2
9
|
20 Jul 2011
|
3
10
|
|
data/lib/finance/amortization.rb
CHANGED
data/lib/finance/cashflows.rb
CHANGED
@@ -1,24 +1,58 @@
|
|
1
1
|
require_relative 'decimal'
|
2
|
+
require_relative 'rates'
|
3
|
+
|
4
|
+
require 'bigdecimal'
|
5
|
+
require 'bigdecimal/newton'
|
6
|
+
include Newton
|
2
7
|
|
3
8
|
module Finance
|
4
9
|
# Provides methods for working with cash flows (collections of transactions)
|
5
10
|
# @api public
|
6
11
|
module Cashflow
|
12
|
+
# Base class for working with Newton's Method.
|
13
|
+
# @api private
|
14
|
+
class Function
|
15
|
+
values = {
|
16
|
+
eps: "1.0e-16",
|
17
|
+
one: "1.0",
|
18
|
+
two: "2.0",
|
19
|
+
ten: "10.0",
|
20
|
+
zero: "0.0"
|
21
|
+
}
|
22
|
+
|
23
|
+
values.each do |key, value|
|
24
|
+
define_method key do
|
25
|
+
BigDecimal.new value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(transactions, function)
|
30
|
+
@transactions = transactions
|
31
|
+
@function = function
|
32
|
+
end
|
33
|
+
|
34
|
+
def values(x)
|
35
|
+
value = @transactions.send(@function, x[0].to_d)
|
36
|
+
[ BigDecimal.new(value.to_s) ]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
7
40
|
# calculate the internal rate of return for a sequence of cash flows
|
8
41
|
# @return [DecNum] the internal rate of return
|
9
42
|
# @example
|
10
43
|
# [-4000,1200,1410,1875,1050].irr #=> 0.143
|
11
44
|
# @see http://en.wikipedia.org/wiki/Internal_rate_of_return
|
12
45
|
# @api public
|
13
|
-
def irr
|
14
|
-
|
46
|
+
def irr
|
47
|
+
func = Function.new(self, :npv)
|
48
|
+
rate = [ func.one ]
|
49
|
+
n = nlsolve( func, rate )
|
50
|
+
rate[0]
|
51
|
+
end
|
15
52
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
rate
|
53
|
+
def method_missing(name, *args, &block)
|
54
|
+
return self.inject(:+) if name.to_s == "sum"
|
55
|
+
super
|
22
56
|
end
|
23
57
|
|
24
58
|
# calculate the net present value of a sequence of cash flows
|
@@ -39,13 +73,39 @@ module Finance
|
|
39
73
|
total
|
40
74
|
end
|
41
75
|
|
42
|
-
#
|
76
|
+
# calculate the internal rate of return for a sequence of cash flows with dates
|
77
|
+
# @return [Rate] the internal rate of return
|
78
|
+
# @example
|
79
|
+
# @transactions = []
|
80
|
+
# @transactions << Transaction.new(-1000, :date => Time.new(1985,01,01))
|
81
|
+
# @transactions << Transaction.new( 600, :date => Time.new(1990,01,01))
|
82
|
+
# @transactions << Transaction.new( 600, :date => Time.new(1995,01,01))
|
83
|
+
# @transactions.xirr(0.6).round(2) #=> Rate("0.024851", :apr, :compounds => :annually)
|
43
84
|
# @api public
|
44
|
-
def
|
45
|
-
|
85
|
+
def xirr(iterations=100)
|
86
|
+
func = Function.new(self, :xnpv)
|
87
|
+
rate = [ func.one ]
|
88
|
+
n = nlsolve( func, rate )
|
89
|
+
Rate.new(rate[0], :apr, :compounds => :annually)
|
46
90
|
end
|
47
91
|
|
48
|
-
|
92
|
+
# calculate the net present value of a sequence of cash flows
|
93
|
+
# @return [DecNum]
|
94
|
+
# @example
|
95
|
+
# @transactions = []
|
96
|
+
# @transactions << Transaction.new(-1000, :date => Time.new(1985,01,01))
|
97
|
+
# @transactions << Transaction.new( 600, :date => Time.new(1990,01,01))
|
98
|
+
# @transactions << Transaction.new( 600, :date => Time.new(1995,01,01))
|
99
|
+
# @transactions.xnpv(0.6).round(2) #=> -937.41
|
100
|
+
# @api public
|
101
|
+
def xnpv(rate)
|
102
|
+
rate = rate.to_d
|
103
|
+
start = self[0].date
|
104
|
+
|
105
|
+
self.inject(0) do |sum, t|
|
106
|
+
n = t.amount / ( (1 + rate) ** ((t.date-start) / 31536000.to_d)) # 365 * 86400
|
107
|
+
sum + n
|
108
|
+
end
|
49
109
|
end
|
50
110
|
end
|
51
111
|
end
|
data/lib/finance/decimal.rb
CHANGED
@@ -1,12 +1,21 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'flt'
|
3
|
+
include Flt
|
4
|
+
|
5
|
+
DecNum.context.define_conversion_from(BigDecimal) do |x, context|
|
6
|
+
DecNum(x.to_s)
|
7
|
+
end
|
8
|
+
|
9
|
+
DecNum.context.define_conversion_to(BigDecimal) do |x|
|
10
|
+
BigDecimal.new(x.to_s)
|
11
|
+
end
|
3
12
|
|
4
13
|
class Numeric
|
5
14
|
def to_d
|
6
|
-
if self.instance_of?
|
15
|
+
if self.instance_of? DecNum
|
7
16
|
self
|
8
17
|
else
|
9
|
-
|
18
|
+
DecNum self.to_s
|
10
19
|
end
|
11
20
|
end
|
12
21
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'cashflows'
|
2
|
+
require_relative 'transaction'
|
3
|
+
|
4
|
+
require 'time'
|
5
|
+
include Finance
|
6
|
+
|
7
|
+
trans = [
|
8
|
+
Transaction.new(-1000, :date => Time.parse("2011-01-01")),
|
9
|
+
Transaction.new(100, :date => Time.parse("2011-02-11")),
|
10
|
+
Transaction.new(100, :date => Time.parse("2011-03-11")),
|
11
|
+
Transaction.new(100, :date => Time.parse("2011-04-11")),
|
12
|
+
Transaction.new(100, :date => Time.parse("2011-05-11")),
|
13
|
+
Transaction.new(100, :date => Time.parse("2011-06-11")),
|
14
|
+
Transaction.new(100, :date => Time.parse("2011-07-11")),
|
15
|
+
Transaction.new(100, :date => Time.parse("2011-08-11")),
|
16
|
+
Transaction.new(100, :date => Time.parse("2011-09-11")),
|
17
|
+
Transaction.new(100, :date => Time.parse("2011-10-11")),
|
18
|
+
Transaction.new(100, :date => Time.parse("2011-11-11")),
|
19
|
+
Transaction.new(100, :date => Time.parse("2011-12-11")),
|
20
|
+
Transaction.new(100, :date => Time.parse("2012-01-11"))
|
21
|
+
# Transaction.new(-100, :date => Time.parse("2011-01-11")),
|
22
|
+
# Transaction.new(150, :date => Time.parse("2012-01-11"))
|
23
|
+
]
|
24
|
+
|
25
|
+
puts trans.xirr.effective.round(9)
|
data/lib/finance/transaction.rb
CHANGED
@@ -11,6 +11,9 @@ module Finance
|
|
11
11
|
# @note this attribute is mainly used in the case of mortgage amortization with no dates
|
12
12
|
# @api public
|
13
13
|
attr_accessor :period
|
14
|
+
# @return [Date] the date of the transaction
|
15
|
+
# @api public
|
16
|
+
attr_accessor :date
|
14
17
|
|
15
18
|
# Set the cash value of the transaction
|
16
19
|
# @return None
|
data/test/test_cashflows.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require_relative '../lib/finance/cashflows.rb'
|
2
|
+
require_relative '../lib/finance/rates.rb'
|
2
3
|
|
3
4
|
require 'flt/d'
|
4
5
|
require 'minitest/unit'
|
5
6
|
require 'shoulda'
|
6
7
|
|
7
8
|
class TestCashflows < Test::Unit::TestCase
|
8
|
-
context "an array of cashflows" do
|
9
|
+
context "an array of numeric cashflows" do
|
9
10
|
should "have an Internal Rate of Return" do
|
10
11
|
assert_equal D("0.143"), [-4000,1200,1410,1875,1050].irr.round(3)
|
11
12
|
end
|
@@ -13,4 +14,20 @@ class TestCashflows < Test::Unit::TestCase
|
|
13
14
|
assert_equal D("49.211"), [-100.0, 60, 60, 60].npv(0.1).round(3)
|
14
15
|
end
|
15
16
|
end
|
17
|
+
context "an array of Transactions" do
|
18
|
+
setup do
|
19
|
+
@xactions=[]
|
20
|
+
@xactions << Transaction.new(-1000, :date => Time.new(1985,01,01))
|
21
|
+
@xactions << Transaction.new( 600, :date => Time.new(1990,01,01))
|
22
|
+
@xactions << Transaction.new( 600, :date => Time.new(1995,01,01))
|
23
|
+
end
|
24
|
+
|
25
|
+
should "have an Internal Rate of Return" do
|
26
|
+
assert_equal D("0.024851"), @xactions.xirr.effective.round(6)
|
27
|
+
end
|
28
|
+
|
29
|
+
should "have a Net Present Value" do
|
30
|
+
assert_equal D("-937.41"), @xactions.xnpv(0.6).round(2)
|
31
|
+
end
|
32
|
+
end
|
16
33
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: finance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 1.0.0
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Bill Kranec
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-09-11 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: flt
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- lib/finance/amortization.rb
|
53
53
|
- lib/finance/cashflows.rb
|
54
54
|
- lib/finance/decimal.rb
|
55
|
+
- lib/finance/gistfile1.rb
|
55
56
|
- lib/finance/interval.rb
|
56
57
|
- lib/finance/rates.rb
|
57
58
|
- lib/finance/transaction.rb
|