pathfinderdev-mortgage_calc 0.1.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/Manifest +11 -0
- data/Rakefile +37 -0
- data/Version.yml +4 -0
- data/features/apr.feature +18 -0
- data/features/step_definitions/apr_steps.rb +7 -0
- data/features/support/env.rb +5 -0
- data/lib/mortgage_calc.rb +9 -0
- data/lib/mortgage_calc/mortgage_util.rb +55 -0
- data/pathfinderdev-mortgage_calc.gemspec +30 -0
- data/spec/mortgage_calc/mortgage_util_spec.rb +38 -0
- data/spec/spec_helper.rb +3 -0
- metadata +69 -0
data/Manifest
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Manifest
|
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
|
+
pathfinderdev-mortgage_calc.gemspec
|
10
|
+
spec/mortgage_calc/mortgage_util_spec.rb
|
11
|
+
spec/spec_helper.rb
|
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("pathfinderdev-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,18 @@
|
|
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 |
|
@@ -0,0 +1,7 @@
|
|
1
|
+
Given /^(.*), (.*), (.*), (.*), (.*)$/ do |loan_amount, fees, points, rate, period|
|
2
|
+
@mortgage_util = MortgageCalc::MortgageUtil.new(Integer(loan_amount), Float(rate), Float(period), Float(fees), Float(points))
|
3
|
+
end
|
4
|
+
|
5
|
+
Then /^the resultant apr should be (.*)$/ do |apr_expected|
|
6
|
+
Float(apr_expected).should be_close(@mortgage_util.apr, 0.001)
|
7
|
+
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,55 @@
|
|
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)
|
6
|
+
self.loan_amount = loan_amount
|
7
|
+
self.interest_rate = interest_rate
|
8
|
+
self.period = period
|
9
|
+
self.lender_fee = lender_fee
|
10
|
+
self.points = points
|
11
|
+
end
|
12
|
+
|
13
|
+
def apr
|
14
|
+
payment_with_fees = calculate_monthly_payment(self.loan_amount + total_fees, monthly_interst_rate, self.period)
|
15
|
+
calculate_apr_with_newton_raphson(self.period, payment_with_fees, self.loan_amount, monthly_interst_rate + 1) * 12 * 100
|
16
|
+
end
|
17
|
+
|
18
|
+
def monthly_payment
|
19
|
+
calculate_monthly_payment(self.loan_amount, monthly_interst_rate, self.period)
|
20
|
+
end
|
21
|
+
|
22
|
+
def total_fees
|
23
|
+
buyer_points = self.points <= 0 ? 0 : self.points
|
24
|
+
self.lender_fee + (self.loan_amount * buyer_points.abs/100)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def monthly_interst_rate
|
29
|
+
self.interest_rate / 100 / 12
|
30
|
+
end
|
31
|
+
|
32
|
+
def calculate_monthly_payment(amount, monthly_rate, period)
|
33
|
+
amount * (monthly_rate/(1 - (1 + monthly_rate)**(-period)))
|
34
|
+
end
|
35
|
+
|
36
|
+
# solves APR
|
37
|
+
# where a = APR/1200, N = period, P = monthly payment, C = loan_amount
|
38
|
+
# calculate APR uses the Newton-Raphson to find the root (the value for 'a' that makes f(a) = 0)
|
39
|
+
# for best performance call this with 'start'= interest rate
|
40
|
+
def calculate_apr_with_newton_raphson(periods, monthly_payment, loan_amount, start=1.1)
|
41
|
+
payment_ratio = monthly_payment / loan_amount
|
42
|
+
f = lambda {|k| (k**(periods + 1) - (k**periods * (payment_ratio + 1)) + payment_ratio)}
|
43
|
+
f_slope = lambda { |k| ((periods + 1) * k**periods) - (periods * (payment_ratio + 1) * k**(periods - 1))}
|
44
|
+
|
45
|
+
k_plus_one = start
|
46
|
+
k = 0.0
|
47
|
+
|
48
|
+
while ((k - 1) * 100000).to_f.floor != ((k_plus_one - 1) * 100000).to_f.floor
|
49
|
+
k = k_plus_one
|
50
|
+
k_plus_one = k - f.call(k) / f_slope.call(k)
|
51
|
+
end
|
52
|
+
(k_plus_one - 1).to_f
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{pathfinderdev-mortgage_calc}
|
5
|
+
s.version = "0.1.1"
|
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-12}
|
10
|
+
s.description = %q{Mortgage utilities}
|
11
|
+
s.email = %q{phertler@pathf.com}
|
12
|
+
s.extra_rdoc_files = ["lib/mortgage_calc.rb", "lib/mortgage_calc/mortgage_util.rb"]
|
13
|
+
s.files = ["Manifest", "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", "pathfinderdev-mortgage_calc.gemspec", "spec/mortgage_calc/mortgage_util_spec.rb", "spec/spec_helper.rb"]
|
14
|
+
s.homepage = %q{http://github.com/pathfinderdev/mortgage_calc}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Pathfinderdev-mortgage_calc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{pathfinderdev-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,38 @@
|
|
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 = MortgageUtil.new(loan_amount + mortgage_util.total_fees, rate, period, 0, 0).monthly_payment
|
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
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pathfinderdev-mortgage_calc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Perry Hertler
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-12 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
|
+
- lib/mortgage_calc.rb
|
24
|
+
- lib/mortgage_calc/mortgage_util.rb
|
25
|
+
files:
|
26
|
+
- Manifest
|
27
|
+
- Rakefile
|
28
|
+
- Version.yml
|
29
|
+
- features/apr.feature
|
30
|
+
- features/step_definitions/apr_steps.rb
|
31
|
+
- features/support/env.rb
|
32
|
+
- lib/mortgage_calc.rb
|
33
|
+
- lib/mortgage_calc/mortgage_util.rb
|
34
|
+
- pathfinderdev-mortgage_calc.gemspec
|
35
|
+
- spec/mortgage_calc/mortgage_util_spec.rb
|
36
|
+
- spec/spec_helper.rb
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/pathfinderdev/mortgage_calc
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --line-numbers
|
44
|
+
- --inline-source
|
45
|
+
- --title
|
46
|
+
- Pathfinderdev-mortgage_calc
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "1.2"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project: pathfinderdev-mortgage_calc
|
64
|
+
rubygems_version: 1.3.5
|
65
|
+
signing_key:
|
66
|
+
specification_version: 3
|
67
|
+
summary: Mortgage utilities
|
68
|
+
test_files: []
|
69
|
+
|