blend_spreadsheet_loan_generator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +7 -0
- data/README.md +36 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/blend_spreadsheet_loan_generator.gemspec +35 -0
- data/exe/bslg +5 -0
- data/lib/blend_spreadsheet_loan_generator.rb +37 -0
- data/lib/blend_spreadsheet_loan_generator/concerns/csv_concern.rb +29 -0
- data/lib/blend_spreadsheet_loan_generator/concerns/spreadsheet_concern.rb +123 -0
- data/lib/blend_spreadsheet_loan_generator/early_repay.rb +163 -0
- data/lib/blend_spreadsheet_loan_generator/formula.rb +211 -0
- data/lib/blend_spreadsheet_loan_generator/generate.rb +73 -0
- data/lib/blend_spreadsheet_loan_generator/init.rb +36 -0
- data/lib/blend_spreadsheet_loan_generator/linear.rb +20 -0
- data/lib/blend_spreadsheet_loan_generator/loan.rb +127 -0
- data/lib/blend_spreadsheet_loan_generator/normal_interests.rb +21 -0
- data/lib/blend_spreadsheet_loan_generator/realistic_interests.rb +19 -0
- data/lib/blend_spreadsheet_loan_generator/restructure.rb +121 -0
- data/lib/blend_spreadsheet_loan_generator/simple_interests.rb +18 -0
- data/lib/blend_spreadsheet_loan_generator/standard.rb +25 -0
- data/lib/blend_spreadsheet_loan_generator/version.rb +13 -0
- metadata +141 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module BlendSpreadsheetLoanGenerator
|
2
|
+
class NormalInterests
|
3
|
+
include SpreadsheetConcern
|
4
|
+
|
5
|
+
attr_accessor :loan
|
6
|
+
def initialize(loan:)
|
7
|
+
@loan = loan
|
8
|
+
end
|
9
|
+
|
10
|
+
def period_rate_formula(*)
|
11
|
+
periods_per_year = excel_float(12.0 / loan.period_duration)
|
12
|
+
"=TAUX.NOMINAL(#{excel_float(loan.rate)};#{periods_per_year}) / #{periods_per_year}"
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def period_fees_rate_formula(*)
|
17
|
+
periods_per_year = excel_float(12.0 / loan.period_duration)
|
18
|
+
"=TAUX.NOMINAL(#{excel_float(loan.fees_rate)};#{periods_per_year}) / #{periods_per_year}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module BlendSpreadsheetLoanGenerator
|
2
|
+
class RealisticInterests
|
3
|
+
include SpreadsheetConcern
|
4
|
+
|
5
|
+
attr_accessor :loan
|
6
|
+
def initialize(loan:)
|
7
|
+
@loan = loan
|
8
|
+
end
|
9
|
+
|
10
|
+
def period_rate_formula(line:)
|
11
|
+
"=#{excel_float(loan.rate)} * ((#{period_leap_days(line)} / 366) + (#{period_non_leap_days(line)} / 365))"
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
def period_fees_rate_formula(*)
|
16
|
+
"=#{excel_float(loan.fees_rate)} * ((#{period_leap_days(line)} / 366) + (#{period_non_leap_days(line)} / 365))"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module BlendSpreadsheetLoanGenerator
|
2
|
+
class Restructure < Dry::CLI::Command
|
3
|
+
include SpreadsheetConcern
|
4
|
+
include CsvConcern
|
5
|
+
|
6
|
+
attr_accessor :loan
|
7
|
+
|
8
|
+
desc "Generate spreadsheet"
|
9
|
+
|
10
|
+
argument :last_paid_term, type: :integer, required: true, desc: 'last paid term, restructuration starts at last_paid + 1'
|
11
|
+
argument :from_path, type: :string, required: true, desc: 'csv to restructure'
|
12
|
+
argument :duration, type: :float, required: true, desc: 'duration post restructuration'
|
13
|
+
argument :rate, type: :float, required: true, desc: 'year rate post restructuration'
|
14
|
+
|
15
|
+
option :period_duration, type: :integer, default: 1, desc: 'duration of a period in months'
|
16
|
+
option :due_on, type: :date, default: Date.today, desc: 'date of the pay day of the first period DD/MM/YYYY'
|
17
|
+
option :deferred_and_capitalized, type: :integer, default: 0, desc: 'periods with no capital or interests paid'
|
18
|
+
option :deferred, type: :integer, default: 0, desc: 'periods with only interests paid'
|
19
|
+
option :type, type: :string, default: 'standard', values: %w[standard linear], desc: 'type of amortization'
|
20
|
+
option :interests_type, type: :string, default: 'simple', values: %w[simple realistic normal], desc: 'type of interests calculations'
|
21
|
+
option :fees_rate, type: :float, default: 0.0, required: true, desc: 'year fees rate'
|
22
|
+
option :starting_capitalized_interests, type: :float, default: 0.0, desc: 'starting capitalized interests (if ongoing loan)'
|
23
|
+
option :starting_capitalized_fees, type: :float, default: 0.0, desc: 'starting capitalized fees (if ongoing loan)'
|
24
|
+
option :target_path, type: :string, default: './', desc: 'where to put the generated csv'
|
25
|
+
|
26
|
+
def call(last_paid_term:, from_path:, duration:, rate:, **options)
|
27
|
+
begin
|
28
|
+
session = GoogleDrive::Session.from_config(
|
29
|
+
File.join(ENV['SPREADSHEET_LOAN_GENERATOR_DIR'], 'config.json')
|
30
|
+
)
|
31
|
+
rescue StandardError => e
|
32
|
+
if ENV['SPREADSHEET_LOAN_GENERATOR_DIR'].blank?
|
33
|
+
puts 'please set SPREADSHEET_LOAN_GENERATOR_DIR'
|
34
|
+
else
|
35
|
+
puts 'Cannot connect to google drive. Did you run slg init CLIENT_ID CLIENT_SECRET ?'
|
36
|
+
end
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
f = CSV.open(from_path)
|
41
|
+
values = f.to_a
|
42
|
+
values.map! { |r| set_types([columns, r].transpose.to_h.with_indifferent_access) }
|
43
|
+
|
44
|
+
last_paid_line = values.index { |term| term[:index] == last_paid_term.to_i }
|
45
|
+
|
46
|
+
starting_capitalized_interests = (
|
47
|
+
values[last_paid_line + 1][:capitalized_interests_start] +
|
48
|
+
values[last_paid_line + 1][:period_calculated_interests]
|
49
|
+
)
|
50
|
+
starting_capitalized_fees = (
|
51
|
+
values[last_paid_line + 1][:capitalized_fees_start] +
|
52
|
+
values[last_paid_line + 1][:period_calculated_fees]
|
53
|
+
)
|
54
|
+
|
55
|
+
due_on = values[last_paid_line + 1] + 1.month
|
56
|
+
|
57
|
+
@loan = Loan.new(
|
58
|
+
amount: values[last_paid_line][:remaining_capital_end],
|
59
|
+
duration: duration,
|
60
|
+
rate: rate,
|
61
|
+
fees_rate: options.fetch(:fees_rate),
|
62
|
+
period_duration: options.fetch(:period_duration),
|
63
|
+
due_on: due_on,
|
64
|
+
deferred_and_capitalized: options.fetch(:deferred_and_capitalized),
|
65
|
+
deferred: options.fetch(:deferred),
|
66
|
+
type: options.fetch(:type),
|
67
|
+
interests_type: options.fetch(:interests_type),
|
68
|
+
starting_capitalized_interests: starting_capitalized_interests,
|
69
|
+
starting_capitalized_fees: starting_capitalized_fees
|
70
|
+
)
|
71
|
+
|
72
|
+
spreadsheet = session.create_spreadsheet(loan.name)
|
73
|
+
worksheet = spreadsheet.add_worksheet(loan.type, loan.duration + 2, columns.count + 1, index: 0)
|
74
|
+
|
75
|
+
@formula = Formula.new(loan: loan)
|
76
|
+
|
77
|
+
apply_formulas(worksheet: worksheet)
|
78
|
+
apply_formats(worksheet: worksheet)
|
79
|
+
|
80
|
+
worksheet.save
|
81
|
+
worksheet.reload
|
82
|
+
|
83
|
+
generate_csv(worksheet: worksheet, target_path: options.fetch(:target_path))
|
84
|
+
|
85
|
+
puts worksheet.human_url
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_types(h)
|
89
|
+
h[:index] = h[:index].to_i
|
90
|
+
|
91
|
+
h[:due_on] = Date.strptime(h[:due_on], '%m/%d/%Y')
|
92
|
+
h[:remaining_capital_start] = h[:remaining_capital_start].to_f
|
93
|
+
h[:remaining_capital_end] = h[:remaining_capital_end].to_f
|
94
|
+
h[:period_theoric_interests] = h[:period_theoric_interests].to_f
|
95
|
+
h[:delta] = h[:delta].to_f
|
96
|
+
h[:accrued_delta] = h[:accrued_delta].to_f
|
97
|
+
h[:amount_to_add] = h[:amount_to_add].to_f
|
98
|
+
h[:period_interests] = h[:period_interests].to_f
|
99
|
+
h[:period_capital] = h[:period_capital].to_f
|
100
|
+
h[:total_paid_capital_end_of_period] = h[:total_paid_capital_end_of_period].to_f
|
101
|
+
h[:total_paid_interests_end_of_period] = h[:total_paid_interests_end_of_period].to_f
|
102
|
+
h[:period_total] = h[:period_total].to_f
|
103
|
+
h[:capitalized_interests_start] = h[:capitalized_interests_start].to_f
|
104
|
+
h[:capitalized_interests_end] = h[:capitalized_interests_end].to_f
|
105
|
+
h[:period_rate] = h[:period_rate].to_f
|
106
|
+
h[:period_calculated_capital] = h[:period_calculated_capital].to_f
|
107
|
+
h[:period_calculated_interests] = h[:period_calculated_interests].to_f
|
108
|
+
h[:period_reimbursed_capitalized_interests] = h[:period_reimbursed_capitalized_interests].to_f
|
109
|
+
h[:period_leap_days] = h[:period_leap_days].to_i
|
110
|
+
h[:period_non_leap_days] = h[:period_non_leap_days].to_i
|
111
|
+
h[:period_fees] = h[:period_fees].to_f
|
112
|
+
h[:period_calculated_fees] = h[:period_calculated_fees].to_f
|
113
|
+
h[:capitalized_fees_start] = h[:capitalized_fees_start].to_f
|
114
|
+
h[:capitalized_fees_end] = h[:capitalized_fees_end].to_f
|
115
|
+
h[:period_reimbursed_capitalized_fees] = h[:period_reimbursed_capitalized_fees].to_f
|
116
|
+
h[:period_fees_rate] = h[:period_fees_rate].to_f
|
117
|
+
|
118
|
+
h
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module BlendSpreadsheetLoanGenerator
|
2
|
+
class SimpleInterests
|
3
|
+
include SpreadsheetConcern
|
4
|
+
|
5
|
+
attr_accessor :loan
|
6
|
+
def initialize(loan:)
|
7
|
+
@loan = loan
|
8
|
+
end
|
9
|
+
|
10
|
+
def period_rate_formula(*)
|
11
|
+
"=#{excel_float(loan.rate)} * #{loan.period_duration} / 12,0"
|
12
|
+
end
|
13
|
+
|
14
|
+
def period_fees_rate_formula(*)
|
15
|
+
"=#{excel_float(loan.fees_rate)} * #{loan.period_duration} / 12,0"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module BlendSpreadsheetLoanGenerator
|
2
|
+
class Standard
|
3
|
+
include SpreadsheetConcern
|
4
|
+
|
5
|
+
attr_accessor :loan
|
6
|
+
|
7
|
+
def initialize(loan:)
|
8
|
+
@loan = loan
|
9
|
+
end
|
10
|
+
|
11
|
+
def period_calculated_capital_formula(line:)
|
12
|
+
amount =
|
13
|
+
if loan.deferred_and_capitalized.zero?
|
14
|
+
excel_float(loan.amount + loan.starting_capitalized_interests + loan.starting_capitalized_fees)
|
15
|
+
else
|
16
|
+
"#{capitalized_interests_end(loan.deferred_and_capitalized + 1)} + #{capitalized_fees_end(loan.deferred_and_capitalized + 1)} + #{excel_float(loan.amount)}"
|
17
|
+
end
|
18
|
+
term_cell = "#{index(line)} - #{loan.total_deferred_duration}"
|
19
|
+
|
20
|
+
params = "#{period_rate(line)};#{term_cell};#{loan.non_deferred_duration};#{amount}"
|
21
|
+
"=-PPMT(#{params})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: blend_spreadsheet_loan_generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- MZiserman
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-05-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
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: csv
|
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: dry-cli
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.6'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: google_drive
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description:
|
84
|
+
email:
|
85
|
+
- martinziserman@gmail.com
|
86
|
+
executables:
|
87
|
+
- bslg
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".travis.yml"
|
94
|
+
- Gemfile
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- bin/console
|
98
|
+
- bin/setup
|
99
|
+
- blend_spreadsheet_loan_generator.gemspec
|
100
|
+
- exe/bslg
|
101
|
+
- lib/blend_spreadsheet_loan_generator.rb
|
102
|
+
- lib/blend_spreadsheet_loan_generator/concerns/csv_concern.rb
|
103
|
+
- lib/blend_spreadsheet_loan_generator/concerns/spreadsheet_concern.rb
|
104
|
+
- lib/blend_spreadsheet_loan_generator/early_repay.rb
|
105
|
+
- lib/blend_spreadsheet_loan_generator/formula.rb
|
106
|
+
- lib/blend_spreadsheet_loan_generator/generate.rb
|
107
|
+
- lib/blend_spreadsheet_loan_generator/init.rb
|
108
|
+
- lib/blend_spreadsheet_loan_generator/linear.rb
|
109
|
+
- lib/blend_spreadsheet_loan_generator/loan.rb
|
110
|
+
- lib/blend_spreadsheet_loan_generator/normal_interests.rb
|
111
|
+
- lib/blend_spreadsheet_loan_generator/realistic_interests.rb
|
112
|
+
- lib/blend_spreadsheet_loan_generator/restructure.rb
|
113
|
+
- lib/blend_spreadsheet_loan_generator/simple_interests.rb
|
114
|
+
- lib/blend_spreadsheet_loan_generator/standard.rb
|
115
|
+
- lib/blend_spreadsheet_loan_generator/version.rb
|
116
|
+
homepage: https://github.com/CapSens/blend_spreadsheet_loan_generator
|
117
|
+
licenses: []
|
118
|
+
metadata:
|
119
|
+
allowed_push_host: https://rubygems.org
|
120
|
+
homepage_uri: https://github.com/CapSens/blend_spreadsheet_loan_generator
|
121
|
+
source_code_uri: https://github.com/CapSens/blend_spreadsheet_loan_generator
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: 2.3.0
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubygems_version: 3.1.4
|
138
|
+
signing_key:
|
139
|
+
specification_version: 4
|
140
|
+
summary: Generate spreadsheets amortization schedules from the command line
|
141
|
+
test_files: []
|