blend_spreadsheet_loan_generator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+
@@ -0,0 +1,13 @@
1
+ require 'dry/cli'
2
+
3
+ module BlendSpreadsheetLoanGenerator
4
+ VERSION = '0.1.0'
5
+
6
+ class Version < Dry::CLI::Command
7
+ desc 'Print version'
8
+
9
+ def call(*)
10
+ puts VERSION
11
+ end
12
+ end
13
+ end
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: []