valuation 0.0.1

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.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2008-09-28
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/License.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 Tim Kofol
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,13 @@
1
+ History.txt
2
+ lib/valuation/cash_flows.rb
3
+ lib/valuation/core_ext.rb
4
+ lib/valuation/interest_rate.rb
5
+ lib/valuation.rb
6
+ License.txt
7
+ Manifest
8
+ Rakefile
9
+ README.textile
10
+ test/test_cash_flows.rb
11
+ test/test_helper.rb
12
+ test/test_interest_rate.rb
13
+ test/test_valuation.rb
data/README.textile ADDED
@@ -0,0 +1,57 @@
1
+ h1. Valuation
2
+
3
+ A Corporate Finance Ruby Gem. For those of you who are sick of using excel for all your finance calculations. Now there is an alternative for you Ruby hackers.
4
+
5
+ h2. Features
6
+
7
+ * Calculate the NPV, FV or IRR of a series of cash flows
8
+ * Convert interest rates from different time periods (APR, EAR)
9
+ * Access functions for Annuity, Perpetuity
10
+
11
+ h2. Examples
12
+
13
+ > require 'valuation'
14
+
15
+ > include Valuation
16
+
17
+ *Cash Flows*
18
+
19
+ > CashFlows.new([10,10,10], :i => 0.10).present_value
20
+
21
+ => 27.3553719008264
22
+
23
+ > CashFlows.new([10,10,10], :i => 0.10).future_value
24
+
25
+ => 33.1
26
+
27
+ > CashFlows.new([-10,10,10]).irr
28
+
29
+ => 0.618033338439153
30
+
31
+ *Interest Rate*
32
+
33
+ > ir = InterestRate.new(0.10)
34
+
35
+ > ir.yearly
36
+
37
+ => 0.10
38
+
39
+ > ir.rate
40
+
41
+ => 0.10
42
+
43
+ > ir = InterestRate.new(0.01, :monthly)
44
+
45
+ > rate.yearly
46
+
47
+ => 0.12682503013197
48
+
49
+ > ir = InterestRate.new(0.12, :apr, :compounded => :monthly)
50
+
51
+ > ir.monthly
52
+
53
+ => 0.01
54
+
55
+ > ir.yearly
56
+
57
+ => 0.12682503013197
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+
3
+ gem 'technicalpickles-echoe'
4
+ require 'echoe'
5
+
6
+ Echoe.new('valuation') do |p|
7
+ p.version = '0.0.1'
8
+ p.author = "Tim Kofol"
9
+ p.email = "tkofol@gmail.com"
10
+ p.summary = "A Ruby Gem For Corporate Finance Calculations."
11
+ end
12
+
13
+
14
+
data/lib/valuation.rb ADDED
@@ -0,0 +1,50 @@
1
+ $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
2
+ require 'valuation/cash_flows'
3
+ require 'valuation/core_ext'
4
+ require 'valuation/interest_rate'
5
+
6
+ module Valuation
7
+
8
+ # Returns the present value of a perpetuity
9
+ #
10
+ # c = Cash flow at time 1
11
+ # i = Interest rate
12
+ # Optional g = Growth rate
13
+ def perpetuity(c, i, g=0)
14
+ c / (i-g).to_f
15
+ end
16
+
17
+ # Returns the present value of an annuity
18
+ #
19
+ # c = Cash flow at time 1
20
+ # i = Interest rate
21
+ # n = The number of time periods
22
+ # options hash
23
+ # :g = Growth Rate Defauls to 0
24
+ # :pv = Defaults to true
25
+ def annuity(c, i, n, options = {})
26
+ options = {:g => 0, :pv => true}.merge(options)
27
+ g = options[:g]
28
+ pv = c * (1/(i-g).to_f) * (1 - ((1+g)/(1+i).to_f) ** n)
29
+ if options[:pv] == true
30
+ pv
31
+ else
32
+ pv * (1+i) ** n
33
+ end
34
+ end
35
+
36
+ # Returns the present value of a stream of cash flows
37
+ # that have an interest rate r
38
+ #
39
+ # c = Array of cash flows
40
+ # r = intrest rate as a decimal
41
+ def npv(c, r)
42
+ npv=0
43
+ c.each_with_index do |c_t,t|
44
+ npv += c_t.to_f / (1.0+r)**t.to_f
45
+ end
46
+ npv
47
+ end
48
+
49
+
50
+ end
@@ -0,0 +1,45 @@
1
+ module Valuation
2
+
3
+ class CashFlows
4
+
5
+ def initialize(flows, options = {})
6
+ @flows = flows
7
+ @i = options[:i] || 0
8
+ end
9
+
10
+ def present_value
11
+ value = 0
12
+ @flows.each_index do |n|
13
+ value += @flows[n] / ((1+@i)**n).to_f
14
+ end
15
+ value
16
+ end
17
+
18
+ def future_value
19
+ value = 0
20
+ @flows.reverse.each_index do |n|
21
+ value += @flows[n] * ((1+@i)**n).to_f
22
+ end
23
+ value
24
+ end
25
+
26
+ def irr(significant_digits=4)
27
+ limit = 10**-(significant_digits)
28
+ estimate = @flows.sum/(@flows.size-1.0)/75.0
29
+ delta = estimate.abs
30
+ n = npv(@flows,estimate)
31
+ while n.abs > limit
32
+ sign = n/n.abs
33
+ if (delta.abs < limit/1000)
34
+ delta=estimate.abs #if we aren't getting anywhere, take a big jump
35
+ return sign/0.0 if (n-@flows[0]).abs < limit
36
+ end
37
+ estimate += (delta/=2) * sign
38
+ n = npv(@flows,estimate)
39
+ end
40
+ estimate
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def sum
3
+ inject(0){|s,v|s+v}
4
+ end
5
+ end
@@ -0,0 +1,72 @@
1
+ include Math
2
+ module Valuation
3
+
4
+ class InterestRate
5
+ attr_accessor :rate
6
+ @@periods = {:yearly => 1.0,
7
+ :semiannually => (1/2.0),
8
+ :bi_annually => (1/2.0),
9
+ :quarterly => (1/4.0),
10
+ :monthly => (1/12.0),
11
+ :weekly => (1/52.0),
12
+ :daily => (1/365.0)}
13
+
14
+ #Setup The Period Methods
15
+ @@periods.each_pair do |period, n|
16
+ define_method "#{period}" do
17
+ convert_period n
18
+ end
19
+ end
20
+
21
+ # Add a unique time period. If you need different time
22
+ # periods that are not provided by default
23
+ #
24
+ # All time periods are measured to a base time period of 1 year
25
+ # so to add an hourly time period
26
+ #
27
+ # InterestRate.add_time_periods(:hourly => (1/(365*24)))
28
+ # This time period will now be available, in the init method.
29
+ # It will also add a instance method hourly
30
+ def self.add_time_periods(periods)
31
+ @@periods.merge(periods)
32
+ periods.each_pair do |period, n|
33
+ define_method "#{period}" do
34
+ convert_period n
35
+ end
36
+ end
37
+ end
38
+
39
+ # All interest rates are converted to EAR rates.
40
+ # So the base time period is one year.
41
+ #
42
+ # 10% EAR rate: InterestRate.new(0.10)
43
+ #
44
+ # 5% EMR rate: InterestRate.new(0.05, :monthly)
45
+ #
46
+ # 12% APR compounded monthly InterestRate.new(0.12, :apr, :compounded => :monthly)
47
+ #
48
+ # Custom Period 5% Compounded every third of the year InterestRate.new(0.05, 1/3.0)
49
+ def initialize(rate, type=:yearly, options = {})
50
+ if type.class.to_s == 'Float'
51
+ @rate = convert_period(1/type, rate)
52
+ elsif type.to_sym == :apr
53
+ options = {:compounded => :yearly}.merge(options)
54
+ factor = @@periods[options[:compounded]]
55
+ @rate = (1 + (rate*factor.to_f))**(1/factor.to_f) - 1
56
+ else
57
+ @rate = convert_period(1/@@periods[type], rate)
58
+ end
59
+ end
60
+
61
+ # The continous interest rate for a given time period t
62
+ def continuous(t)
63
+ E ** (rate * t) - 1
64
+ end
65
+
66
+ private
67
+ def convert_period(n, rate=@rate)
68
+ ((1 + rate) ** n) - 1
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+ include Valuation
3
+
4
+ class TestCashFlows < Test::Unit::TestCase
5
+
6
+ def test_cash_flow_pv
7
+ cf = CashFlows.new([10,10,10], :i => 0.10)
8
+ assert_in_delta 27.3553, cf.present_value, 0.01
9
+ end
10
+
11
+ def test_cash_flow_fv
12
+ cf = CashFlows.new([10,10,10], :i => 0.10)
13
+ assert_in_delta 33.1, cf.future_value, 0.01
14
+ end
15
+
16
+ def test_irr
17
+ cf = CashFlows.new([-10,10,10])
18
+ assert_in_delta 0.61803, cf.irr, 0.01
19
+ end
20
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/valuation'
@@ -0,0 +1,54 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+ include Valuation
3
+
4
+ class TestInterestRate < Test::Unit::TestCase
5
+
6
+ def test_yearly
7
+ rate = InterestRate.new(0.10)
8
+ assert_in_delta 0.10, rate.yearly, 0.0001
9
+ end
10
+
11
+ def test_bi_annually
12
+ rate = InterestRate.new(0.10)
13
+ assert_in_delta 0.0488088481701, rate.bi_annually, 0.0001
14
+ end
15
+
16
+ def test_monthly
17
+ rate = InterestRate.new(0.10)
18
+ assert_in_delta 0.00797411, rate.monthly, 0.0001
19
+ end
20
+
21
+ def test_add_monthly_rate
22
+ rate = InterestRate.new(0.01, :monthly)
23
+ assert_in_delta 0.12682503013197, rate.yearly, 0.0001
24
+ end
25
+
26
+ def test_add_new_time_period
27
+ InterestRate.add_time_periods({:once_in_a_blue_moon => 3.41666666666667})
28
+ rate = InterestRate.new(0.10)
29
+ assert_not_nil rate.once_in_a_blue_moon
30
+ rate2 = InterestRate.new(0.10)
31
+ assert_not_nil rate.once_in_a_blue_moon
32
+ end
33
+
34
+ def test_apr_monthly
35
+ rate = InterestRate.new(0.12, :apr, :compounded => :monthly)
36
+ assert_in_delta 0.01, rate.monthly, 0.0001
37
+ assert_in_delta 0.12682503013197, rate.rate, 0.0001
38
+ end
39
+
40
+ def test_apr_semi_annually
41
+ rate = InterestRate.new(0.12, :apr, :compounded => :semiannually)
42
+ assert_in_delta 0.06, rate.semiannually, 0.0001
43
+ end
44
+
45
+ def test_continuous_interest_rate
46
+ rate = InterestRate.new(0.05)
47
+ assert_in_delta 0.28402, rate.continuous(5), 0.0001
48
+ end
49
+
50
+ def test_custom_interest_rate
51
+ rate = InterestRate.new(0.05, (1/3.0))
52
+ assert_in_delta 0.157625, rate.rate, 0.0001
53
+ end
54
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+ include Valuation
3
+
4
+ class TestValuation < Test::Unit::TestCase
5
+
6
+ def test_perpetuity
7
+ assert_in_delta 20.0, perpetuity(20, 1), 0.01
8
+ assert_in_delta 20.0, perpetuity(20.0, 1), 0.01
9
+ assert_in_delta 200.0, perpetuity(20, 0.1), 0.01
10
+ end
11
+
12
+ def test_growing_prepetuity
13
+ assert_in_delta 20.0, perpetuity(20, 2, 1), 0.01
14
+ assert_in_delta 200.0, perpetuity(20, 0.5, 0.4), 0.01
15
+ end
16
+
17
+ def test_present_value_annuity
18
+ assert_in_delta 31698.654, annuity(10000, 0.10, 4) , 0.01
19
+ end
20
+
21
+ def test_present_value_growing_annuity
22
+ assert_in_delta 52.6971, annuity(10, 0.10, 7, :g => 0.03), 0.01
23
+ end
24
+
25
+ def test_future_value_annuity
26
+ assert_in_delta 46410.0, annuity(10000, 0.10, 4, :pv => false), 0.01
27
+ end
28
+
29
+ def test_future_value_growing_annuity
30
+ assert_in_delta 102.69, annuity(10, 0.10, 7, :g => 0.03, :pv => false), 0.01
31
+ end
32
+
33
+ def test_npv
34
+ assert_in_delta 24.86965, npv([0,10,10, 10], 0.10), 0.01
35
+ end
36
+ end
data/valuation.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{valuation}
3
+ s.version = "0.0.1"
4
+
5
+ #s.required_rubygems_version = Gem::Requirement.new("= 1.2") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["Tim Kofol"]
7
+ s.date = %q{2008-10-19}
8
+ s.description = %q{A Ruby Gem For Corporate Finance Calculations.}
9
+ s.email = %q{tkofol@gmail.com}
10
+ s.extra_rdoc_files = ["lib/valuation/cash_flows.rb", "lib/valuation/core_ext.rb", "lib/valuation/interest_rate.rb", "lib/valuation.rb", "README.textile"]
11
+ s.files = ["History.txt", "lib/valuation/cash_flows.rb", "lib/valuation/core_ext.rb", "lib/valuation/interest_rate.rb", "lib/valuation.rb", "License.txt", "Manifest", "Rakefile", "README.textile", "test/test_cash_flows.rb", "test/test_helper.rb", "test/test_interest_rate.rb", "test/test_valuation.rb", "valuation.gemspec"]
12
+ s.has_rdoc = true
13
+ s.homepage = %q{}
14
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Valuation", "--main", "README.textile"]
15
+ s.require_paths = ["lib"]
16
+ s.rubyforge_project = %q{valuation}
17
+ #s.rubygems_version = %q{1.2.0}
18
+ s.summary = %q{A Ruby Gem For Corporate Finance Calculations.}
19
+ s.test_files = ["test/test_cash_flows.rb", "test/test_helper.rb", "test/test_interest_rate.rb", "test/test_valuation.rb"]
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 2
24
+
25
+ if current_version >= 3 then
26
+ s.add_development_dependency(%q<echoe>, [">= 0"])
27
+ else
28
+ s.add_dependency(%q<echoe>, [">= 0"])
29
+ end
30
+ else
31
+ s.add_dependency(%q<echoe>, [">= 0"])
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: valuation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Tim Kofol
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-19 00:00:00 +08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: echoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: A Ruby Gem For Corporate Finance Calculations.
26
+ email: tkofol@gmail.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - lib/valuation/cash_flows.rb
33
+ - lib/valuation/core_ext.rb
34
+ - lib/valuation/interest_rate.rb
35
+ - lib/valuation.rb
36
+ - README.textile
37
+ files:
38
+ - History.txt
39
+ - lib/valuation/cash_flows.rb
40
+ - lib/valuation/core_ext.rb
41
+ - lib/valuation/interest_rate.rb
42
+ - lib/valuation.rb
43
+ - License.txt
44
+ - Manifest
45
+ - Rakefile
46
+ - README.textile
47
+ - test/test_cash_flows.rb
48
+ - test/test_helper.rb
49
+ - test/test_interest_rate.rb
50
+ - test/test_valuation.rb
51
+ - valuation.gemspec
52
+ has_rdoc: true
53
+ homepage: ""
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --line-numbers
59
+ - --inline-source
60
+ - --title
61
+ - Valuation
62
+ - --main
63
+ - README.textile
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ requirements: []
79
+
80
+ rubyforge_project: valuation
81
+ rubygems_version: 1.3.5
82
+ signing_key:
83
+ specification_version: 2
84
+ summary: A Ruby Gem For Corporate Finance Calculations.
85
+ test_files:
86
+ - test/test_cash_flows.rb
87
+ - test/test_helper.rb
88
+ - test/test_interest_rate.rb
89
+ - test/test_valuation.rb