interest_calc 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/LICENSE +21 -0
- data/README.md +2 -0
- data/interest_calc.gemspec +25 -0
- data/lib/interest_calc/calculators/open_line_calculator.rb +159 -0
- data/lib/interest_calc/version.rb +3 -0
- data/lib/interest_calc.rb +2 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7b9270952b4db924510e32a7767e8938c0087bfb2808f5f91083f469fdc2f0f9
|
4
|
+
data.tar.gz: 7f35803f477a96ce89afef7fe150c02e7b53de92c33dd2027521e67420d29206
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7b433d79cc59d0d6a6368205d92d54fff8c877511693b9ce6e51cd230f0c2a04d074c4526d3a82b112ba0836d17c9b6be64d1c8f068c6c72fbe70c3c9a4f4812
|
7
|
+
data.tar.gz: f09d1dc9b2b0290b281f43e707f42737fe364d57d2a979f428ad811f57324a2f9cf6c63e1c588877eec12f1bc5d77b08b321f05f8a17bfebf4dce297fbad616e
|
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2022 yas4891
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'lib/interest_calc/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'interest_calc'
|
5
|
+
s.version = InterestCalc::VERSION
|
6
|
+
s.summary = "Useful methods around calculating interests, especially for open lines of credit"
|
7
|
+
s.description = "Helps you calculate interest for open lines of credit"
|
8
|
+
s.authors = ["Christoph Engelhardt"]
|
9
|
+
s.email = 'christoph@christophengelhardt.com'
|
10
|
+
|
11
|
+
# Specify which files should be added to the gem when it is released.
|
12
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
13
|
+
s.files = Dir.chdir(File.expand_path(__dir__)) do
|
14
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
15
|
+
end
|
16
|
+
|
17
|
+
s.homepage = 'https://rubygems.org/gems/interest_calc'
|
18
|
+
s.license = 'MIT'
|
19
|
+
|
20
|
+
s.add_dependency('ostruct')
|
21
|
+
s.add_dependency('active_support')
|
22
|
+
|
23
|
+
s.add_dependency('interest_days')
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/core_ext'
|
4
|
+
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
|
8
|
+
module InterestCalc
|
9
|
+
class OpenLineCalculator
|
10
|
+
|
11
|
+
attr_accessor :interest_rate
|
12
|
+
|
13
|
+
def initialize(interest_rate = 0.05)
|
14
|
+
@interest_rate = interest_rate
|
15
|
+
end
|
16
|
+
|
17
|
+
# calculates the interest per month for an open line of credit
|
18
|
+
def calculate(data)
|
19
|
+
|
20
|
+
return_values = []
|
21
|
+
nitem = nil
|
22
|
+
changes = data[:changes]
|
23
|
+
|
24
|
+
if changes.nil? || changes.count < 1
|
25
|
+
raise Exception.new "No changes to the line of credit provided. Please provide an array of changes data[:changes]"
|
26
|
+
end
|
27
|
+
|
28
|
+
changes = changes.reverse # allows us to use #pop
|
29
|
+
citem = changes.pop
|
30
|
+
cdate = citem[:date]
|
31
|
+
camount = citem[:amount]
|
32
|
+
|
33
|
+
puts "#{self.class.name}##{__method__}:calculation starts: #{cdate.at_beginning_of_month}"
|
34
|
+
|
35
|
+
|
36
|
+
return_values << {year: cdate.year,
|
37
|
+
month: cdate.month,
|
38
|
+
interest: self.calculate_interest(camount, cdate)
|
39
|
+
}
|
40
|
+
|
41
|
+
# after this point the calculations for the first month are done
|
42
|
+
|
43
|
+
cdate = cdate.at_beginning_of_month # normalize value to always be the first of the month
|
44
|
+
i = 0
|
45
|
+
|
46
|
+
# at the start changes won't be empty and
|
47
|
+
# when all the changes have been popped from the stack nitem won't be empty
|
48
|
+
while(!changes.empty? || !nitem.nil?)
|
49
|
+
puts
|
50
|
+
puts
|
51
|
+
puts "#{self.class.name}##{__method__}: RUN #{i}"
|
52
|
+
i+=1
|
53
|
+
# only pop the next item if the previously popped item was used
|
54
|
+
nitem = nitem || changes.pop
|
55
|
+
ndate = nitem[:date]
|
56
|
+
cdate = cdate.next_month
|
57
|
+
new_return_value = {year: cdate.year, month: cdate.month}
|
58
|
+
# the whole month needs to be calculated at the old amount
|
59
|
+
if(ndate.at_beginning_of_month != cdate)
|
60
|
+
puts "#{self.class.name}##{__method__}: FULL month calculation"
|
61
|
+
return_values << {
|
62
|
+
year: cdate.year,
|
63
|
+
month: cdate.month,
|
64
|
+
interest: self.calculate_interest(camount,cdate)
|
65
|
+
}
|
66
|
+
else # withdrawal/deposit within this month
|
67
|
+
puts "#{self.class.name}##{__method__}: SPLIT month calculation"
|
68
|
+
# calculate for first half of month with the old amount
|
69
|
+
|
70
|
+
interest = calculate_interest(camount, cdate, ndate )
|
71
|
+
puts "#{self.class.name}##{__method__}: amount changing. OLD:#{camount} -- change:#{nitem[:amount]} -- NEW:#{camount + nitem[:amount]}"
|
72
|
+
# change the calculation amount by adding the withdrawal/deposit
|
73
|
+
camount += nitem[:amount]
|
74
|
+
|
75
|
+
# calculate interest for 2nd half of month
|
76
|
+
interest += calculate_interest(camount, ndate)
|
77
|
+
|
78
|
+
return_values << {
|
79
|
+
year: cdate.year,
|
80
|
+
month: cdate.month,
|
81
|
+
interest: interest
|
82
|
+
}
|
83
|
+
|
84
|
+
# reset these variables to pop the next changes-item from the stack
|
85
|
+
ndate = nil
|
86
|
+
nitem = nil
|
87
|
+
|
88
|
+
# do NOT change cdate as this is already on the 1st of the month and will
|
89
|
+
# increase in the while loop
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
end # while !changes.empty?
|
95
|
+
cdate = cdate.next_month
|
96
|
+
# add one final month after the last month with changes
|
97
|
+
# so that users know how much interest is per month going forward
|
98
|
+
return_values << {year: cdate.year,
|
99
|
+
month: cdate.month,
|
100
|
+
interest: self.calculate_interest(camount, cdate)
|
101
|
+
}
|
102
|
+
|
103
|
+
return return_values
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def print(data)
|
109
|
+
values = calculate data
|
110
|
+
|
111
|
+
puts "--------------------------------------"
|
112
|
+
puts "| year-month | interest |"
|
113
|
+
puts "--------------------------------------"
|
114
|
+
values.each do |item|
|
115
|
+
ym = "#{item[:year]}-#{item[:month]}".ljust(16)
|
116
|
+
i = item[:interest].to_f.round(2).to_s.rjust(19)
|
117
|
+
puts "|#{ym}|#{i}|"
|
118
|
+
end
|
119
|
+
puts "--------------------------------------"
|
120
|
+
return values
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
# calculates the interest for current time until either the end of the month (if no end_date given)
|
125
|
+
# or the given end_date
|
126
|
+
def calculate_interest(camount, cdate, end_date = nil)
|
127
|
+
if camount.nil?
|
128
|
+
raise "amount can't be nil"
|
129
|
+
end
|
130
|
+
|
131
|
+
puts "#{self.class.name}##{__method__}: date:#{cdate} -- end_date:#{end_date}"
|
132
|
+
# calculate until end of month if no end_date given.
|
133
|
+
# Add one day to include last day in calculations
|
134
|
+
end_date = end_date || (cdate.at_end_of_month)
|
135
|
+
|
136
|
+
puts "#{self.class.name}##{__method__}: interest rate: #{@interest_rate}"
|
137
|
+
|
138
|
+
result = interest_days_counter_factor(cdate, end_date) * @interest_rate * camount
|
139
|
+
|
140
|
+
puts "#{self.class.name}##{__method__}: calculated interest: #{result}"
|
141
|
+
return result
|
142
|
+
end
|
143
|
+
|
144
|
+
# returns the days counter factor based on European act/360 calculation strategy
|
145
|
+
def interest_days_counter_factor(date, end_date)
|
146
|
+
|
147
|
+
# use European style of calculation where any date on the 31st is
|
148
|
+
# counted as 30st
|
149
|
+
date = date - 1.day if 31 == date.mday
|
150
|
+
end_date = end_date - 1.day if 31 == end_date.mday
|
151
|
+
|
152
|
+
# add 1 to count the first day as well
|
153
|
+
days = ((end_date - date).to_i + 1)
|
154
|
+
factor = days / 360.0
|
155
|
+
puts "#{self.class.name}##{__method__}: days #{days}-- factor:#{factor}"
|
156
|
+
factor
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: interest_calc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christoph Engelhardt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ostruct
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: active_support
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: interest_days
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Helps you calculate interest for open lines of credit
|
56
|
+
email: christoph@christophengelhardt.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- ".gitignore"
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
- interest_calc.gemspec
|
65
|
+
- lib/interest_calc.rb
|
66
|
+
- lib/interest_calc/calculators/open_line_calculator.rb
|
67
|
+
- lib/interest_calc/version.rb
|
68
|
+
homepage: https://rubygems.org/gems/interest_calc
|
69
|
+
licenses:
|
70
|
+
- MIT
|
71
|
+
metadata: {}
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
require_paths:
|
75
|
+
- lib
|
76
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
requirements: []
|
87
|
+
rubygems_version: 3.2.15
|
88
|
+
signing_key:
|
89
|
+
specification_version: 4
|
90
|
+
summary: Useful methods around calculating interests, especially for open lines of
|
91
|
+
credit
|
92
|
+
test_files: []
|