finance_math 0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +30 -11
- data/finance_math.gemspec +4 -4
- data/lib/finance_math.rb +3 -2
- data/lib/finance_math/loan.rb +37 -34
- data/lib/{finance_math/version.rb → version.rb} +1 -1
- data/spec/lib/loan_spec.rb +53 -13
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9b7d65feab360a3535bf1578c17326c237249e6
|
4
|
+
data.tar.gz: 591d90ccad4311cf1fa67a5479b1a3b7995c1ca6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9645d21dd559a86f61f55f417f1ae640581b4a96c0a99f0a75966e527612ba74a33824fc1fbc1b12194486da5d4147069367ff3025bb3287323ec32d759e73db
|
7
|
+
data.tar.gz: 3db071b7b84bd12b44fba98cbce71c97ffcca778e707839d0f1b671ea29a8591e88acc6103a7627ae89c9dd7384ff0a6facaa3b4a86105b0912a4d857add5310
|
data/README.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
![Gem Version](https://img.shields.io/badge/finance_math-0
|
1
|
+
![Gem Version](https://img.shields.io/badge/finance_math-1.0-blue.svg)
|
2
2
|
[![Build Status](https://semaphoreapp.com/api/v1/projects/869d7630-55d3-46e5-9dc2-03d0d1cfecfe/363108/shields_badge.svg)](https://semaphoreapp.com/kolosek/finance_math)
|
3
3
|
[![Code Climate](https://codeclimate.com/github/kolosek/finance_math/badges/gpa.svg)](https://codeclimate.com/github/kolosek/finance_math)
|
4
4
|
|
5
5
|
|
6
6
|
## What is FinanceMath?
|
7
7
|
|
8
|
-
FinanceMath is a Ruby library for mapping Exel functions.
|
8
|
+
FinanceMath is a Ruby library for mapping Loan based Exel functions. It deals with problem of calculating the PMT and APR functions. It implements advanced usage, taking into account bank fees, fee for for each payment and currency protection (if currency other than $ USD). Highly precise with high speed of execution.
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
|
@@ -28,29 +28,47 @@ Create an instance, and pass parameters for nominal annual rate, duration (in mo
|
|
28
28
|
Loan.new(10.5, 12, 15000)
|
29
29
|
```
|
30
30
|
|
31
|
+
## Advanced Usage
|
32
|
+
|
33
|
+
Create an instance, and pass parameters for nominal annual rate, duration (in months), and amount of loan, and additional values such as bank fee, currency protection, and fee for each monthly payment.
|
34
|
+
|
35
|
+
Defaults are bank fee = 5, currency protection = 3, so please update if you need other values.
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
|
39
|
+
Loan.new(10.5, 12, 15000, 5.1, 2.75, 25)
|
40
|
+
```
|
41
|
+
|
31
42
|
## Functions
|
32
43
|
|
33
44
|
This is the list of available functions.
|
34
45
|
|
35
|
-
|
46
|
+
### Loan.pmt
|
36
47
|
|
37
48
|
Calculates the periodic payment for an annuity investment based on constant-amount periodic payments and a constant interest rate.
|
38
49
|
|
39
50
|
```ruby
|
40
51
|
|
41
|
-
loan = Loan.new(10
|
52
|
+
loan = Loan.new(10, 12, 1000)
|
42
53
|
loan.pmt
|
54
|
+
# 87.9158872300099
|
55
|
+
|
43
56
|
```
|
44
57
|
|
45
|
-
|
58
|
+
### Loan.apr
|
46
59
|
|
47
60
|
Calculates the Annual Percentage Rate.
|
48
61
|
|
49
62
|
```ruby
|
50
63
|
|
51
|
-
loan = Loan.new(
|
64
|
+
loan = Loan.new(13, 12, 10000)
|
65
|
+
loan.apr
|
66
|
+
#29.179538647635006
|
52
67
|
|
68
|
+
loan = Loan.new(15, 36, 10000, 5, 3, 10)
|
53
69
|
loan.apr
|
70
|
+
#23.964418264624054
|
71
|
+
|
54
72
|
```
|
55
73
|
|
56
74
|
## Contributing
|
@@ -65,12 +83,13 @@ loan.apr
|
|
65
83
|
|
66
84
|
Please cover with tests your pull requests
|
67
85
|
|
68
|
-
## License
|
69
|
-
|
70
|
-
MIT License. See LICENSE for details.
|
71
|
-
|
72
86
|
## Copyright
|
73
87
|
|
74
88
|
Copyright (c) 2014-2015 Nebojsa Zoric, and Kolosek, Inc. (http://kolosek.com)
|
75
89
|
|
76
|
-
|
90
|
+
###Follow up @nebojsaz and @kolosek
|
91
|
+
|
92
|
+
|
93
|
+
## License
|
94
|
+
|
95
|
+
MIT License. See LICENSE for details.
|
data/finance_math.gemspec
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require '
|
4
|
+
require 'version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "finance_math"
|
8
8
|
spec.version = FinanceMath::VERSION
|
9
9
|
spec.authors = ["Nebojsa Zoric"]
|
10
10
|
spec.email = ["office@kolosek.com"]
|
11
|
-
spec.summary = %q{Finance library for Ruby.}
|
12
|
-
spec.description = %q{Implementation of Loan/Mortgage functions in Ruby language}
|
13
|
-
spec.homepage = ""
|
11
|
+
spec.summary = %q{Finance and mortgage library for Ruby.}
|
12
|
+
spec.description = %q{Implementation of Loan/Mortgage functions in Ruby language. APR function and PMT function. In calculations it includes implementation of bank fee, marketplace fee, fees for each payment to provide the most precise calculation at very high speed. }
|
13
|
+
spec.homepage = "http://kolosek.com/gems/finance_math"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
data/lib/finance_math.rb
CHANGED
data/lib/finance_math/loan.rb
CHANGED
@@ -28,9 +28,11 @@ module FinanceMath
|
|
28
28
|
|
29
29
|
# @return [DecNum] P principal
|
30
30
|
# @api public
|
31
|
-
attr_reader :principal
|
32
|
-
|
31
|
+
attr_reader :principal
|
33
32
|
|
33
|
+
# @return [Float] fee
|
34
|
+
# @api public
|
35
|
+
attr_reader :fee
|
34
36
|
|
35
37
|
# create a new Loan instance
|
36
38
|
# @return [Loan]
|
@@ -44,8 +46,8 @@ module FinanceMath
|
|
44
46
|
# @see http://en.wikipedia.org/wiki/Nominal_interest_rate
|
45
47
|
# @api public
|
46
48
|
|
47
|
-
def initialize(nominal_rate, duration, amount, structure_fee=5, currency_protection=3)
|
48
|
-
@nominal_rate, @amount, @duration, @structure_fee, @currency_protection = nominal_rate.to_f, amount, duration, structure_fee.to_f, currency_protection.to_f
|
49
|
+
def initialize(nominal_rate, duration, amount, structure_fee=5, currency_protection=3, fee=0)
|
50
|
+
@nominal_rate, @amount, @duration, @structure_fee, @currency_protection, @fee = nominal_rate.to_f, amount, duration, structure_fee.to_f, currency_protection.to_f, fee.to_f
|
49
51
|
@principal = principal_calculation
|
50
52
|
@monthly_rate = @nominal_rate / 100 / 12
|
51
53
|
end
|
@@ -55,35 +57,7 @@ module FinanceMath
|
|
55
57
|
end
|
56
58
|
|
57
59
|
def apr
|
58
|
-
|
59
|
-
n = duration.to_f / 12
|
60
|
-
q = (duration > 12) ? 12 : duration
|
61
|
-
i = pmt_base * n * q / principal - 1
|
62
|
-
#puts "n: #{n} q: #{q} i: #{i} pmt: #{pmt_base}"
|
63
|
-
find(pmt_base, n, q, i)
|
64
|
-
end
|
65
|
-
|
66
|
-
def principal_base(m, n, q, i)
|
67
|
-
m * ( 1 - ( 1 + ( i / q ) ) ** (- n * q) ) * q / i
|
68
|
-
end
|
69
|
-
|
70
|
-
def near(a, b)
|
71
|
-
r = 0.1
|
72
|
-
return ((a - r) < b && (a + r) > b) || ((b - r) < a && (b + r) > a)
|
73
|
-
end
|
74
|
-
|
75
|
-
def find(pmt_base, n, q, i)
|
76
|
-
p = principal_base(pmt_base, n, q, i)
|
77
|
-
|
78
|
-
return i if near(p, principal)
|
79
|
-
|
80
|
-
if p < principal
|
81
|
-
i -= 0.00001
|
82
|
-
elsif p > principal
|
83
|
-
i += 0.00001
|
84
|
-
end
|
85
|
-
|
86
|
-
find(pmt_base, n, q, i)
|
60
|
+
@apr ||= calculate_apr
|
87
61
|
end
|
88
62
|
|
89
63
|
protected
|
@@ -107,7 +81,36 @@ module FinanceMath
|
|
107
81
|
private
|
108
82
|
|
109
83
|
def principal_calculation
|
110
|
-
amount * (1 - currency_protection/100 - structure_fee / 100 )
|
84
|
+
amount * (1 - currency_protection/100 - structure_fee / 100 ) - fee * duration
|
85
|
+
end
|
86
|
+
|
87
|
+
# solves APR
|
88
|
+
# [a (1 + a)^N] / [(1 + a)^N - 1] - P/C = 0
|
89
|
+
# where a = APR/1200, N = duration, P = monthly payment, C = loan_amount
|
90
|
+
# Newton-Raphson finds root (the value for 'a' that makes f(a) = 0)
|
91
|
+
def calculate_apr
|
92
|
+
payment_ratio = pmt / principal_calculation
|
93
|
+
duration = @duration
|
94
|
+
f = lambda {|k| (k**(duration + 1) - (k**duration * (payment_ratio + 1)) + payment_ratio)}
|
95
|
+
f_deriv = lambda { |k| ((duration + 1) * k**duration) - (duration * (payment_ratio + 1) * k**(duration - 1))}
|
96
|
+
|
97
|
+
root = newton_raphson(f, f_deriv, monthly_rate + 1)
|
98
|
+
100 * 12 * (root -1).to_f
|
99
|
+
end
|
100
|
+
|
101
|
+
# 'start' is the monthly_rate, Newton Raphson will find the apr root very quickly
|
102
|
+
# k1 = k0 - f(k0)/f'(k0)
|
103
|
+
# k_plus_one = k - f(k)/f_deriv(k) f_deriv should be an positive number!
|
104
|
+
# We find the k-intercept of the tangent line at point k_plus_one and compare k to k_plus_one.
|
105
|
+
# This is repeated until a sufficiently accurate value is reached, which can be specified with the 'precision' parameter
|
106
|
+
def newton_raphson(f, f_deriv, start, precision = 5)
|
107
|
+
k_plus_one = start
|
108
|
+
k = 0.0
|
109
|
+
while ((k - 1) * 10**precision).to_f.floor != ((k_plus_one - 1) * 10**precision).to_f.floor
|
110
|
+
k = k_plus_one
|
111
|
+
k_plus_one = k - f.call(k) / f_deriv.call(k).abs
|
112
|
+
end
|
113
|
+
k_plus_one
|
111
114
|
end
|
112
115
|
end
|
113
116
|
end
|
data/spec/lib/loan_spec.rb
CHANGED
@@ -37,33 +37,73 @@ describe Loan do
|
|
37
37
|
loan = Loan.new(0, 6, 10000)
|
38
38
|
expect(loan.pmt).to eq(1666.6666666666667)
|
39
39
|
end
|
40
|
+
|
41
|
+
it "should return correct pmt value" do
|
42
|
+
loan = Loan.new(13, 90, 1000000)
|
43
|
+
expect(loan.pmt).to eq(17449.90775727763)
|
44
|
+
end
|
40
45
|
end
|
41
46
|
|
42
|
-
context ".apr" do
|
47
|
+
context ".apr, edge cases" do
|
43
48
|
|
44
49
|
it "should return correct apr value" do
|
45
|
-
loan = Loan.new(16, 24,
|
46
|
-
expect(loan.apr).to eq(
|
50
|
+
loan = Loan.new(16, 24, 9200)
|
51
|
+
expect(loan.apr).to eq(24.699310868498614)
|
47
52
|
end
|
48
53
|
|
49
54
|
it "should return correct apr value" do
|
50
55
|
loan = Loan.new(13, 24, 10000)
|
51
|
-
expect(loan.apr).to eq(
|
56
|
+
expect(loan.apr).to eq(21.589972932434698)
|
52
57
|
end
|
53
58
|
|
54
59
|
it "should return correct apr value" do
|
55
60
|
loan = Loan.new(13, 18, 10000)
|
56
|
-
expect(loan.apr).to eq(
|
61
|
+
expect(loan.apr).to eq(24.1815502466296)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should return correct apr value" do
|
65
|
+
loan = Loan.new(13, 12, 10000)
|
66
|
+
expect(loan.apr).to eq(29.179538647635006)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should return correct apr value" do
|
70
|
+
loan = Loan.new(13, 6, 10000)
|
71
|
+
expect(loan.apr).to eq(42.82076503747119)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should return correct apr value" do
|
75
|
+
loan = Loan.new(13, 36, 10000)
|
76
|
+
expect(loan.apr).to eq(18.93638316167774)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should return correct apr value" do
|
80
|
+
loan = Loan.new(13, 90, 10000000)
|
81
|
+
expect(loan.apr).to eq(15.690778147507167)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should return correct apr value" do
|
85
|
+
loan = Loan.new(13, 90, 50000000)
|
86
|
+
expect(loan.apr).to eq(15.690778147507167)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should return correct apr value" do
|
90
|
+
loan = Loan.new(13, 1, 50000000)
|
91
|
+
expect(loan.apr).to eq(118.47826151517138)
|
57
92
|
end
|
58
93
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
94
|
+
it "should return correct apr value" do
|
95
|
+
loan = Loan.new(80, 1, 1000)
|
96
|
+
expect(loan.apr).to eq(191.30434783476406)
|
97
|
+
end
|
63
98
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
99
|
+
it "should return correct apr value" do
|
100
|
+
loan = Loan.new(36, 200, 500)
|
101
|
+
expect(loan.apr).to eq(39.173057290003044)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should return correct apr value" do
|
105
|
+
loan = Loan.new(15, 36, 10000, 5, 3, 10)
|
106
|
+
expect(loan.apr).to eq(23.964418264624054)
|
107
|
+
end
|
68
108
|
end
|
69
109
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: finance_math
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nebojsa Zoric
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,7 +52,10 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
description: Implementation of Loan/Mortgage functions in Ruby language
|
55
|
+
description: 'Implementation of Loan/Mortgage functions in Ruby language. APR function
|
56
|
+
and PMT function. In calculations it includes implementation of bank fee, marketplace
|
57
|
+
fee, fees for each payment to provide the most precise calculation at very high
|
58
|
+
speed. '
|
56
59
|
email:
|
57
60
|
- office@kolosek.com
|
58
61
|
executables: []
|
@@ -67,10 +70,10 @@ files:
|
|
67
70
|
- finance_math.gemspec
|
68
71
|
- lib/finance_math.rb
|
69
72
|
- lib/finance_math/loan.rb
|
70
|
-
- lib/
|
73
|
+
- lib/version.rb
|
71
74
|
- spec/lib/loan_spec.rb
|
72
75
|
- spec/spec_helper.rb
|
73
|
-
homepage:
|
76
|
+
homepage: http://kolosek.com/gems/finance_math
|
74
77
|
licenses:
|
75
78
|
- MIT
|
76
79
|
metadata: {}
|
@@ -93,7 +96,7 @@ rubyforge_project:
|
|
93
96
|
rubygems_version: 2.2.2
|
94
97
|
signing_key:
|
95
98
|
specification_version: 4
|
96
|
-
summary: Finance library for Ruby.
|
99
|
+
summary: Finance and mortgage library for Ruby.
|
97
100
|
test_files:
|
98
101
|
- spec/lib/loan_spec.rb
|
99
102
|
- spec/spec_helper.rb
|