blend_spreadsheet_loan_generator 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 +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: []
|