mortgage_calc 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest ADDED
@@ -0,0 +1,11 @@
1
+ README.rdoc
2
+ Rakefile
3
+ Version.yml
4
+ features/apr.feature
5
+ features/step_definitions/apr_steps.rb
6
+ features/support/env.rb
7
+ lib/mortgage_calc.rb
8
+ lib/mortgage_calc/mortgage_util.rb
9
+ spec/mortgage_calc/mortgage_util_spec.rb
10
+ spec/spec_helper.rb
11
+ Manifest
data/README.rdoc ADDED
@@ -0,0 +1,47 @@
1
+ = mortgage_calc
2
+
3
+ Calculates mortgage APR, monthly payments, and fees.
4
+
5
+ == INSTALL
6
+ $ sudo gem install gemcutter
7
+ $ gem tumble
8
+ $ sudo gem install mortgage_calc
9
+ or add the following to your <b>environment.rb</b>
10
+ config.gem 'mortgage_calc'
11
+
12
+ ==Example:
13
+ loan_amount = 350000
14
+ interest_rate = 4.75
15
+ period = 30 * 12
16
+ lender_fee = 800
17
+ points = 1.0
18
+
19
+ mort_calc = MortgageCalc::MortgageUtil.new(loan_amount, interest_rate, period, lender_fee, points)
20
+
21
+ mort_calc.apr
22
+ mort_calc.monthly_payment
23
+ mort_calc.monthly_payment_with_fees
24
+ mort_calc.total_fees
25
+
26
+ ==Formulas used
27
+ ===Monthly payment with fees
28
+ P = [(C + E) r (1 + r)^N]/[(1 + r)^N - 1]
29
+
30
+ P = monthly payment
31
+ C = Loan amount
32
+ r = Interest rate
33
+ N = Period in months
34
+ E = Lender fees
35
+
36
+ ===Monthly payment without fees is calculated like above with E = 0.
37
+
38
+ ===APR
39
+ [a (1 + a)^N] / [(1 + a)^N - 1] - P/C = 0
40
+ a = A/1200
41
+ N = Period in months
42
+ P = Monthly payment
43
+ C = Loan amount
44
+
45
+ ===Total fees
46
+ Total fees are calculated simply by adding Lender fees to the points paid by borrower.
47
+ T = E + P(C)
data/Rakefile ADDED
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+ require 'spec'
5
+ require 'cucumber'
6
+ require 'lib/mortgage_calc'
7
+
8
+ require 'cucumber/rake/task'
9
+ require 'spec/rake/spectask'
10
+
11
+ Echoe.new("mortgage_calc", MortgageCalc::VERSION) do |p|
12
+ p.description = "Mortgage utilities"
13
+ p.url = "http://github.com/pathfinderdev/mortgage_calc"
14
+ p.author = "Perry Hertler"
15
+ p.email = "phertler@pathf.com"
16
+ p.ignore_pattern = ["tmp/*", "script/*, .idea/*"]
17
+ p.development_dependencies = []
18
+ end
19
+
20
+ Dir["#File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
21
+
22
+ spec_files = Rake::FileList["spec/**/*_spec.rb"]
23
+
24
+ desc "Run specs"
25
+ Spec::Rake::SpecTask.new do |t|
26
+ t.spec_files = spec_files
27
+ t.spec_opts = ["-c"]
28
+ end
29
+
30
+ Cucumber::Rake::Task.new(:features) do |t|
31
+ t.cucumber_opts = "features --format progress"
32
+ end
33
+
34
+ task :default => [:spec, :features]
35
+
36
+
37
+
data/Version.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 5
@@ -0,0 +1,19 @@
1
+ Feature Calculate APR
2
+ In order to show sorted APR search results
3
+ As a person navigating OMT
4
+ I want to see correct APRs
5
+
6
+ Scenario Outline: Calculate APR
7
+ Given <loan_amount>, <fees>, <points>, <rate>, <period>
8
+ Then the resultant apr should be <apr>
9
+
10
+ Scenarios: with APR fields
11
+ | loan_amount | fees | points | rate | period | apr |
12
+ | 125000 | 5000 | 0 | 6.5 | 360 | 6.881 |
13
+ | 125000 | 5000 | -1.25 | 6.5 | 360 | 6.881 |
14
+ | 400000 | 1200 | 0 | 5.25 | 180 | 5.296 |
15
+ | 125000 | 811 | 0.375 | 6.125 | 360 | 6.221 |
16
+ | 125000 | 811 | -0.375 | 6.5 | 360 | 6.562 |
17
+ | 100000 | 1000 | 0 | 7.0 | 360 | 7.099 |
18
+ | 100000 | 0 | 0 | 7.0 | 360 | 7.0 |
19
+ | 250000 | 3135 | 2.125 | 6.5 | 360 | 6.822 |
@@ -0,0 +1,7 @@
1
+ Given /^(.*), (.*), (.*), (.*), (.*)$/ do |loan_amount, fees, points, rate, period|
2
+ @mortgage_util = MortgageCalc::MortgageUtil.new(Integer(loan_amount), Float(rate), Integer(period), Float(fees), Float(points))
3
+ end
4
+
5
+ Then /^the resultant apr should be (.*)$/ do |apr_expected|
6
+ @mortgage_util.apr.should be_close(Float(apr_expected), 0.001)
7
+ end
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__),"..","..","lib")
2
+ require 'mortgage_calc'
3
+ require 'spec/expectations'
4
+ require 'spec/matchers'
5
+ require 'spec/stubs/cucumber'
@@ -0,0 +1,58 @@
1
+ module MortgageCalc
2
+ class MortgageUtil
3
+ attr_accessor :loan_amount, :interest_rate, :period, :lender_fee, :points
4
+
5
+ def initialize(loan_amount, interest_rate, period=360, lender_fee=0, points=0.0)
6
+ self.loan_amount = Float(loan_amount.to_s)
7
+ self.interest_rate = Float(interest_rate.to_s)
8
+ self.period = Integer(period.to_s)
9
+ self.lender_fee = lender_fee
10
+ self.points = Float(points.to_s)
11
+ end
12
+
13
+ def apr
14
+ calculate_apr_with_newton_raphson(self.period, monthly_payment_with_fees, self.loan_amount, monthly_interst_rate + 1)
15
+ end
16
+
17
+ def monthly_payment
18
+ calculate_monthly_payment(self.loan_amount, monthly_interst_rate, self.period)
19
+ end
20
+
21
+ def monthly_payment_with_fees
22
+ calculate_monthly_payment(self.loan_amount + total_fees, monthly_interst_rate, self.period)
23
+ end
24
+
25
+ def total_fees
26
+ buyer_points = self.points <= 0 ? 0 : self.points
27
+ self.lender_fee + (self.loan_amount * buyer_points.abs/100)
28
+ end
29
+
30
+ private
31
+ def monthly_interst_rate
32
+ self.interest_rate / 100 / 12
33
+ end
34
+
35
+ def calculate_monthly_payment(amount, monthly_rate, period)
36
+ amount * (monthly_rate/(1 - (1 + monthly_rate)**(-period)))
37
+ end
38
+
39
+ # solves APR
40
+ # where a = APR/1200, N = period, P = monthly payment, C = loan_amount
41
+ # calculate APR uses the Newton-Raphson to find the root (the value for 'a' that makes f(a) = 0)
42
+ # for best performance call this with 'start'= interest rate
43
+ def calculate_apr_with_newton_raphson(periods, monthly_payment, loan_amount, start=1.1)
44
+ payment_ratio = monthly_payment / loan_amount
45
+ f = lambda {|k| (k**(periods + 1) - (k**periods * (payment_ratio + 1)) + payment_ratio)}
46
+ f_slope = lambda { |k| ((periods + 1) * k**periods) - (periods * (payment_ratio + 1) * k**(periods - 1))}
47
+
48
+ k_plus_one = start
49
+ k = 0.0
50
+
51
+ while ((k - 1) * 100000).to_f.floor != ((k_plus_one - 1) * 100000).to_f.floor
52
+ k = k_plus_one
53
+ k_plus_one = k - f.call(k) / f_slope.call(k)
54
+ end
55
+ 100 * 12 * (k_plus_one - 1).to_f
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,9 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
2
+ require 'yaml'
3
+
4
+ module MortgageCalc
5
+ version = YAML.load_file(File.dirname(__FILE__) + "/../Version.yml")
6
+ VERSION = "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
7
+ end
8
+
9
+ require 'mortgage_calc/mortgage_util'
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{mortgage_calc}
5
+ s.version = "0.1.5"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Perry Hertler"]
9
+ s.date = %q{2010-02-13}
10
+ s.description = %q{Mortgage utilities}
11
+ s.email = %q{phertler@pathf.com}
12
+ s.extra_rdoc_files = ["README.rdoc", "lib/mortgage_calc.rb", "lib/mortgage_calc/mortgage_util.rb"]
13
+ s.files = ["README.rdoc", "Rakefile", "Version.yml", "features/apr.feature", "features/step_definitions/apr_steps.rb", "features/support/env.rb", "lib/mortgage_calc.rb", "lib/mortgage_calc/mortgage_util.rb", "spec/mortgage_calc/mortgage_util_spec.rb", "spec/spec_helper.rb", "Manifest", "mortgage_calc.gemspec"]
14
+ s.homepage = %q{http://github.com/pathfinderdev/mortgage_calc}
15
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Mortgage_calc", "--main", "README.rdoc"]
16
+ s.require_paths = ["lib"]
17
+ s.rubyforge_project = %q{mortgage_calc}
18
+ s.rubygems_version = %q{1.3.5}
19
+ s.summary = %q{Mortgage utilities}
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 3
24
+
25
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
+ else
27
+ end
28
+ else
29
+ end
30
+ end
@@ -0,0 +1,61 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper")
2
+
3
+ module MortgageCalc
4
+ describe MortgageUtil do
5
+ def assert_monthly_apr_payment_matches(loan_amount, rate, period, fee, points)
6
+ mortgage_util = MortgageUtil.new(loan_amount, rate, period, fee, points)
7
+ monthly_payment_with_fees = mortgage_util.monthly_payment_with_fees
8
+ monthly_payment_from_apr = MortgageUtil.new(loan_amount, mortgage_util.apr, period, 0, 0).monthly_payment
9
+ monthly_payment_with_fees.should be_close(monthly_payment_from_apr, 0.01)
10
+ end
11
+
12
+ context "with valid MortgageUtil" do
13
+ before(:all) do
14
+ @mortgage_util = MortgageUtil.new(100000, 6.0, 360, 1200, 1.25)
15
+ @mortgage_util_with_apr_as_rate = MortgageUtil.new(100000, @mortgage_util.apr, 360, 1200, 1.25)
16
+ end
17
+ it "should have proper monthly interest rate" do
18
+ @mortgage_util.send(:monthly_interst_rate).should == 0.005
19
+ end
20
+ it "should have proper monthly payment" do
21
+ @mortgage_util.monthly_payment.should be_close(599.55, 0.001)
22
+ end
23
+ it "should have proper total fees" do
24
+ @mortgage_util.total_fees.should be_close(2450, 0.001)
25
+ end
26
+ it "should have proper APR" do
27
+ @mortgage_util.apr.should be_close(6.22726, 0.00001)
28
+ end
29
+ end
30
+ it "should calculate original monthly payment from APR" do
31
+ assert_monthly_apr_payment_matches(300000, 6.5, 360, 1200, 1.25)
32
+ assert_monthly_apr_payment_matches(300000, 6.5, 360, 0, 0)
33
+ assert_monthly_apr_payment_matches(400000, 1.1, 180, 1200, 1.25)
34
+ assert_monthly_apr_payment_matches(300000, 6.5, 360, 0, 7.25)
35
+ assert_monthly_apr_payment_matches(300000, 6.5, 360, 10000, 7.25)
36
+ end
37
+ end
38
+ context "ignore lender points paid to broker" do
39
+ it "the fee should not include negative points" do
40
+ mortgage_util = MortgageUtil.new(100000, 6.0, 360, 1200, -1.25)
41
+ 1200.should == mortgage_util.total_fees
42
+ end
43
+ end
44
+ context "initialize convert to best types" do
45
+ before(:all) do
46
+ @mortgage_util = MortgageUtil.new('100000', '6.0', 360, 1200, '-1.25')
47
+ end
48
+ it "should convert rate to float if necessary" do
49
+ @mortgage_util.interest_rate.type.should == Float
50
+ end
51
+ it "should convert points to float if necessary" do
52
+ @mortgage_util.points.type.should == Float
53
+ end
54
+ it "should convert loan_amount to float if necessary" do
55
+ @mortgage_util.loan_amount.type.should == Float
56
+ end
57
+ it "should convert period to integer if necessary" do
58
+ @mortgage_util.period.type.should == Fixnum
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH << File.join(File.dirname(__FILE__),"..","lib")
2
+ require 'spec'
3
+ require 'mortgage_calc'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mortgage_calc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.5
5
+ platform: ruby
6
+ authors:
7
+ - Perry Hertler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-02-13 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Mortgage utilities
17
+ email: phertler@pathf.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - lib/mortgage_calc.rb
25
+ - lib/mortgage_calc/mortgage_util.rb
26
+ files:
27
+ - README.rdoc
28
+ - Rakefile
29
+ - Version.yml
30
+ - features/apr.feature
31
+ - features/step_definitions/apr_steps.rb
32
+ - features/support/env.rb
33
+ - lib/mortgage_calc.rb
34
+ - lib/mortgage_calc/mortgage_util.rb
35
+ - spec/mortgage_calc/mortgage_util_spec.rb
36
+ - spec/spec_helper.rb
37
+ - Manifest
38
+ - mortgage_calc.gemspec
39
+ has_rdoc: true
40
+ homepage: http://github.com/pathfinderdev/mortgage_calc
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --line-numbers
46
+ - --inline-source
47
+ - --title
48
+ - Mortgage_calc
49
+ - --main
50
+ - README.rdoc
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "1.2"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project: mortgage_calc
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Mortgage utilities
72
+ test_files: []
73
+