loan_creator 0.10.0 → 0.11.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 +4 -4
- data/lib/loan_creator/bullet.rb +13 -5
- data/lib/loan_creator/common.rb +12 -42
- data/lib/loan_creator/linear.rb +15 -2
- data/lib/loan_creator/standard.rb +1 -1
- data/lib/loan_creator/time_helper.rb +62 -0
- data/lib/loan_creator/timetable.rb +4 -0
- data/lib/loan_creator/uncapitalized_bullet.rb +1 -1
- data/lib/loan_creator/version.rb +1 -1
- data/lib/loan_creator.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f7d2dcd74aa8a01bb25577eaf1c6ea05daba9eef071d76119d7ad00773fe0f7
|
|
4
|
+
data.tar.gz: ed40fe1ff5610cbef9bc5c5c92f31095a0eaa6ad745a8f194861e65d5b8e316b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16256ac04047a1b505e37dddec9299d5409d1940dfe2d7271ebc0cc5207bfb5301f3375fbf3f793cb5a70dbacab3e5a7c3c6bd87d07c6449faeba2dd73368f71
|
|
7
|
+
data.tar.gz: 4fe77879f1dfa61adecaf5d30dcf2dd5d6c501c1d82b75232cd106ffa827c440d52326a37c25bc939f445010ab7c4d256527e92edef2124d3c6ed8369337c0eb
|
data/lib/loan_creator/bullet.rb
CHANGED
|
@@ -24,7 +24,7 @@ module LoanCreator
|
|
|
24
24
|
def compute_last_term(timetable)
|
|
25
25
|
@crd_end_of_period = bigd('0')
|
|
26
26
|
@due_interests_beginning_of_period = @due_interests_end_of_period
|
|
27
|
-
@period_interests = @due_interests_end_of_period + compute_capitalized_interests(
|
|
27
|
+
@period_interests = @due_interests_end_of_period + compute_capitalized_interests(timetable)
|
|
28
28
|
@due_interests_end_of_period = 0
|
|
29
29
|
@period_capital = @crd_beginning_of_period
|
|
30
30
|
@total_paid_capital_end_of_period += @period_capital
|
|
@@ -32,14 +32,22 @@ module LoanCreator
|
|
|
32
32
|
@period_amount_to_pay = @period_capital + @period_interests
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def compute_capitalized_interests(
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
def compute_capitalized_interests(timetable)
|
|
36
|
+
if multi_part_interests_calculation && term_dates? && (timetable_term_dates[timetable.current_index] + 1.year) < @due_on
|
|
37
|
+
multi_part_interests(
|
|
38
|
+
timetable_term_dates[timetable.current_index],
|
|
39
|
+
@due_on,
|
|
40
|
+
annual_interests_rate,
|
|
41
|
+
amount_to_capitalize
|
|
42
|
+
)
|
|
43
|
+
else
|
|
44
|
+
compute_period_generated_interests(periodic_interests_rate(timetable_term_dates[timetable.current_index], @due_on))
|
|
45
|
+
end
|
|
38
46
|
end
|
|
39
47
|
|
|
40
48
|
def compute_term(timetable)
|
|
41
49
|
@due_interests_beginning_of_period = @due_interests_end_of_period
|
|
42
|
-
@due_interests_end_of_period += compute_capitalized_interests(
|
|
50
|
+
@due_interests_end_of_period += compute_capitalized_interests(timetable)
|
|
43
51
|
end
|
|
44
52
|
end
|
|
45
53
|
end
|
data/lib/loan_creator/common.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module LoanCreator
|
|
2
2
|
class Common
|
|
3
3
|
extend BorrowerTimetable
|
|
4
|
+
include TimeHelper
|
|
4
5
|
|
|
5
6
|
PERIODS_IN_MONTHS = {
|
|
6
7
|
month: 1,
|
|
@@ -30,9 +31,11 @@ module LoanCreator
|
|
|
30
31
|
deferred_in_periods: 0,
|
|
31
32
|
interests_start_date: nil,
|
|
32
33
|
initial_values: {},
|
|
33
|
-
realistic_durations: false
|
|
34
|
+
realistic_durations: false,
|
|
35
|
+
multi_part_interests_calculation: true
|
|
34
36
|
}.freeze
|
|
35
37
|
|
|
38
|
+
|
|
36
39
|
attr_reader *REQUIRED_ATTRIBUTES
|
|
37
40
|
attr_reader *OPTIONAL_ATTRIBUTES.keys
|
|
38
41
|
|
|
@@ -47,9 +50,9 @@ module LoanCreator
|
|
|
47
50
|
prepare_custom_term_dates if term_dates?
|
|
48
51
|
end
|
|
49
52
|
|
|
50
|
-
def periodic_interests_rate(
|
|
53
|
+
def periodic_interests_rate(start_date, end_date)
|
|
51
54
|
if realistic_durations?
|
|
52
|
-
|
|
55
|
+
compute_realistic_periodic_interests_rate(start_date, end_date, annual_interests_rate)
|
|
53
56
|
else
|
|
54
57
|
@periodic_interests_rate ||=
|
|
55
58
|
annual_interests_rate.div(12 / PERIODS_IN_MONTHS[period], BIG_DECIMAL_DIGITS).div(100, BIG_DECIMAL_DIGITS)
|
|
@@ -101,7 +104,7 @@ module LoanCreator
|
|
|
101
104
|
|
|
102
105
|
def set_attributes
|
|
103
106
|
required_attributes.each { |k| instance_variable_set(:"@#{k}", @options.fetch(k)) }
|
|
104
|
-
OPTIONAL_ATTRIBUTES.each { |k,v| instance_variable_set(:"@#{k}", @options.fetch(k, v)) }
|
|
107
|
+
OPTIONAL_ATTRIBUTES.each { |k, v| instance_variable_set(:"@#{k}", @options.fetch(k, v)) }
|
|
105
108
|
end
|
|
106
109
|
|
|
107
110
|
def validate(key, &block)
|
|
@@ -236,43 +239,6 @@ module LoanCreator
|
|
|
236
239
|
(interests_start_date && interests_start_date < term_zero_date) && !term_dates?
|
|
237
240
|
end
|
|
238
241
|
|
|
239
|
-
def leap_days_count(date, relative_to_date:)
|
|
240
|
-
start_year = relative_to_date.year
|
|
241
|
-
end_year = date.year
|
|
242
|
-
|
|
243
|
-
(start_year..end_year).sum do |year|
|
|
244
|
-
next 0 unless Date.gregorian_leap?(year)
|
|
245
|
-
|
|
246
|
-
start_date =
|
|
247
|
-
if start_year == year
|
|
248
|
-
relative_to_date
|
|
249
|
-
else
|
|
250
|
-
Date.new(year - 1, 12, 31)
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
end_date =
|
|
254
|
-
if end_year == year
|
|
255
|
-
date
|
|
256
|
-
else
|
|
257
|
-
Date.new(year, 12, 31)
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
end_date - start_date
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
def compute_realistic_periodic_interests_rate_percentage_for(date, relative_to_date:)
|
|
265
|
-
total_days = date - relative_to_date
|
|
266
|
-
leap_days = bigd(leap_days_count(date, relative_to_date: relative_to_date))
|
|
267
|
-
non_leap_days = bigd(total_days - leap_days)
|
|
268
|
-
|
|
269
|
-
annual_interests_rate.mult(
|
|
270
|
-
leap_days.div(366, BIG_DECIMAL_DIGITS) +
|
|
271
|
-
non_leap_days.div(365, BIG_DECIMAL_DIGITS),
|
|
272
|
-
BIG_DECIMAL_DIGITS
|
|
273
|
-
)
|
|
274
|
-
end
|
|
275
|
-
|
|
276
242
|
def realistic_durations?
|
|
277
243
|
term_dates? || @realistic_durations.present?
|
|
278
244
|
end
|
|
@@ -301,7 +267,11 @@ module LoanCreator
|
|
|
301
267
|
end
|
|
302
268
|
|
|
303
269
|
def compute_period_generated_interests(interests_rate)
|
|
304
|
-
|
|
270
|
+
amount_to_capitalize.mult(interests_rate, BIG_DECIMAL_DIGITS)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def amount_to_capitalize
|
|
274
|
+
@crd_beginning_of_period + @due_interests_beginning_of_period
|
|
305
275
|
end
|
|
306
276
|
end
|
|
307
277
|
end
|
data/lib/loan_creator/linear.rb
CHANGED
|
@@ -23,12 +23,25 @@ module LoanCreator
|
|
|
23
23
|
@last_period = last_period?(idx)
|
|
24
24
|
@deferred_period = @index <= deferred_in_periods
|
|
25
25
|
@due_on = timetable_term_dates[timetable.next_index]
|
|
26
|
-
computed_periodic_interests_rate = periodic_interests_rate(@due_on, relative_to_date: timetable_term_dates[timetable.next_index - 1])
|
|
27
26
|
|
|
28
27
|
# Reminder: CRD beginning of period = CRD end of period **of previous period**
|
|
29
28
|
@crd_beginning_of_period = @crd_end_of_period
|
|
30
29
|
@due_interests_beginning_of_period = @due_interests_end_of_period
|
|
31
|
-
|
|
30
|
+
|
|
31
|
+
@period_theoric_interests = (
|
|
32
|
+
# if period is more than a year
|
|
33
|
+
if multi_part_interests_calculation && term_dates? && (timetable_term_dates[timetable.current_index] + 1.year) < @due_on
|
|
34
|
+
multi_part_interests(
|
|
35
|
+
timetable_term_dates[timetable.current_index],
|
|
36
|
+
@due_on,
|
|
37
|
+
annual_interests_rate,
|
|
38
|
+
amount_to_capitalize
|
|
39
|
+
)
|
|
40
|
+
else
|
|
41
|
+
period_theoric_interests(periodic_interests_rate(timetable_term_dates[timetable.current_index], @due_on))
|
|
42
|
+
end
|
|
43
|
+
)
|
|
44
|
+
|
|
32
45
|
@delta_interests = @period_theoric_interests - @period_theoric_interests.round(2)
|
|
33
46
|
@accrued_delta_interests += @delta_interests
|
|
34
47
|
@amount_to_add = bigd(
|
|
@@ -24,7 +24,7 @@ module LoanCreator
|
|
|
24
24
|
@last_period = last_period?(idx)
|
|
25
25
|
@deferred_period = @index <= deferred_in_periods
|
|
26
26
|
@due_on = timetable_term_dates[timetable.next_index]
|
|
27
|
-
computed_periodic_interests_rate = periodic_interests_rate(
|
|
27
|
+
computed_periodic_interests_rate = periodic_interests_rate(timetable_term_dates[timetable.current_index], @due_on)
|
|
28
28
|
|
|
29
29
|
@crd_beginning_of_period = @crd_end_of_period
|
|
30
30
|
@period_theoric_interests = period_theoric_interests(@index, computed_periodic_interests_rate)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module LoanCreator
|
|
2
|
+
module TimeHelper
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
def leap_days_count(start_date, end_date)
|
|
7
|
+
start_year = start_date.year
|
|
8
|
+
# mostly no op but allows to skip one iteration if end date is january 1st
|
|
9
|
+
end_year = (end_date - 1.day).year
|
|
10
|
+
|
|
11
|
+
(start_year..end_year).sum do |year|
|
|
12
|
+
next 0 unless Date.gregorian_leap?(year)
|
|
13
|
+
|
|
14
|
+
current_start_date =
|
|
15
|
+
if start_year == year
|
|
16
|
+
start_date
|
|
17
|
+
else
|
|
18
|
+
Date.new(year, 1, 1)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
current_end_date =
|
|
22
|
+
if end_year == year
|
|
23
|
+
end_date
|
|
24
|
+
else
|
|
25
|
+
Date.new(year + 1, 1, 1)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
current_end_date - current_start_date
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def compute_realistic_periodic_interests_rate(start_date, end_date, annual_interests_rate)
|
|
33
|
+
total_days = end_date - start_date
|
|
34
|
+
leap_days = bigd(leap_days_count(start_date, end_date))
|
|
35
|
+
non_leap_days = bigd(total_days - leap_days)
|
|
36
|
+
|
|
37
|
+
annual_interests_rate.mult(
|
|
38
|
+
leap_days.div(366, BIG_DECIMAL_DIGITS) +
|
|
39
|
+
non_leap_days.div(365, BIG_DECIMAL_DIGITS),
|
|
40
|
+
BIG_DECIMAL_DIGITS
|
|
41
|
+
).div(100, BIG_DECIMAL_DIGITS)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# for terms spanning more than a year,
|
|
45
|
+
# we capitalize each years until the last one which behaves normally
|
|
46
|
+
def multi_part_interests(start_date, end_date, annual_interests_rate, amount_to_capitalize)
|
|
47
|
+
duration_in_days = end_date - start_date
|
|
48
|
+
leap_days = bigd(leap_days_count(start_date, end_date))
|
|
49
|
+
non_leap_days = bigd(duration_in_days - leap_days)
|
|
50
|
+
|
|
51
|
+
ratio = non_leap_days.div(365, BIG_DECIMAL_DIGITS) + leap_days.div(366, BIG_DECIMAL_DIGITS)
|
|
52
|
+
full_years, year_part = ratio.divmod(1)
|
|
53
|
+
rate = annual_interests_rate.div(100, BIG_DECIMAL_DIGITS)
|
|
54
|
+
|
|
55
|
+
total = amount_to_capitalize.mult((1 + rate)**full_years, BIG_DECIMAL_DIGITS)
|
|
56
|
+
.mult(1 + rate * year_part, BIG_DECIMAL_DIGITS)
|
|
57
|
+
|
|
58
|
+
total - amount_to_capitalize
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -33,7 +33,7 @@ module LoanCreator
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def compute_interests(due_date, timetable)
|
|
36
|
-
computed_periodic_interests_rate = periodic_interests_rate(
|
|
36
|
+
computed_periodic_interests_rate = periodic_interests_rate(timetable_term_dates[timetable.current_index], due_date)
|
|
37
37
|
amount.mult(bigd(computed_periodic_interests_rate), BIG_DECIMAL_DIGITS)
|
|
38
38
|
end
|
|
39
39
|
|
data/lib/loan_creator/version.rb
CHANGED
data/lib/loan_creator.rb
CHANGED
|
@@ -8,6 +8,7 @@ module LoanCreator
|
|
|
8
8
|
BIG_DECIMAL_DIGITS = 14
|
|
9
9
|
|
|
10
10
|
autoload :ExcelFormulas, 'loan_creator/excel_formulas'
|
|
11
|
+
autoload :TimeHelper, 'loan_creator/time_helper'
|
|
11
12
|
autoload :BorrowerTimetable, 'loan_creator/borrower_timetable'
|
|
12
13
|
autoload :Common, 'loan_creator/common'
|
|
13
14
|
autoload :Standard, 'loan_creator/standard'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: loan_creator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- thibaulth
|
|
@@ -12,7 +12,7 @@ authors:
|
|
|
12
12
|
autorequire:
|
|
13
13
|
bindir: exe
|
|
14
14
|
cert_chain: []
|
|
15
|
-
date: 2021-
|
|
15
|
+
date: 2021-11-03 00:00:00.000000000 Z
|
|
16
16
|
dependencies:
|
|
17
17
|
- !ruby/object:Gem::Dependency
|
|
18
18
|
name: bundler
|
|
@@ -178,6 +178,7 @@ files:
|
|
|
178
178
|
- lib/loan_creator/standard.rb
|
|
179
179
|
- lib/loan_creator/term.rb
|
|
180
180
|
- lib/loan_creator/term_dates_validator.rb
|
|
181
|
+
- lib/loan_creator/time_helper.rb
|
|
181
182
|
- lib/loan_creator/timetable.rb
|
|
182
183
|
- lib/loan_creator/uncapitalized_bullet.rb
|
|
183
184
|
- lib/loan_creator/version.rb
|