finance 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|