Class: Finrb::Amortization
- Inherits:
-
Object
- Object
- Finrb::Amortization
- Defined in:
- lib/finrb/amortization.rb,
sig/finrb.rbs
Overview
the Amortization class provides an interface for working with loan amortizations.
Defined Under Namespace
Classes: Entry
Instance Attribute Summary collapse
-
#amount_financed ⇒ Flt::DecNum
readonly
Principal balance including any financed origination fee.
-
#balance ⇒ Flt::DecNum
readonly
The balance of the loan at the end of the amortization period (usually zero).
-
#balloon ⇒ Flt::DecNum
readonly
Contractual principal settled as a balloon in the final period.
-
#finance_origination_fee ⇒ Boolean
(also: #finance_origination_fee?)
readonly
Returns the value of attribute finance_origination_fee.
-
#interest_only_periods ⇒ Integer
readonly
Number of leading periods that pay interest but no scheduled principal.
-
#net_proceeds ⇒ Flt::DecNum
readonly
Cash made available to the borrower after an unfinanced fee.
-
#origination_fee ⇒ Flt::DecNum
readonly
Fee charged when the loan is originated.
-
#payment ⇒ Flt::DecNum
readonly
The required monthly payment.
-
#principal ⇒ Flt::DecNum
readonly
The principal amount of the loan.
-
#rates ⇒ Array
readonly
The interest rates used for calculating the amortization.
-
#schedule ⇒ Array<Entry>
readonly
Immutable period-by-period loan breakdown.
Class Method Summary collapse
-
.payment(principal, rate, periods, balloon: 0) ⇒ Flt::DecNum
The periodic payment due on a loan.
Instance Method Summary collapse
-
#==(other) ⇒ Numeric
compare two Amortization instances.
-
#additional_payments ⇒ Array
The amount of any additional payments in each period.
-
#duration ⇒ Integer
The time required to pay off the loan, in months.
-
#initialize(principal, *rates, balloon: 0, interest_only_periods: 0, origination_fee: 0, finance_origination_fee: false, &block) ⇒ Amortization
constructor
create a new Amortization instance.
- #inspect ⇒ String
-
#interest ⇒ Array
The amount of interest charged in each period.
-
#payments ⇒ Array
The amount of the payment in each period.
Constructor Details
#initialize(principal, *rates, balloon: 0, interest_only_periods: 0, origination_fee: 0, finance_origination_fee: false, &block) ⇒ Amortization
create a new Amortization instance
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/finrb/amortization.rb', line 117 def initialize(principal, *rates, balloon: 0, interest_only_periods: 0, origination_fee: 0, finance_origination_fee: false, &block) @principal = Validation.positive_decimal(principal, name: 'principal', message: 'principal must be positive.') @origination_fee = Validation.non_negative_decimal(origination_fee, name: 'origination fee') raise(ArgumentError, 'finance_origination_fee must be true or false.') unless [true, false].include?(finance_origination_fee) raise(ArgumentError, 'an unfinanced origination_fee must be less than principal.') if !finance_origination_fee && @origination_fee >= @principal @finance_origination_fee = finance_origination_fee @amount_financed = @principal + (finance_origination_fee ? @origination_fee : 0) @net_proceeds = @principal - (finance_origination_fee ? 0 : @origination_fee) @balloon = Validation.decimal(balloon, name: 'balloon') raise(ArgumentError, 'balloon must be non-negative and less than amount financed.') if @balloon.negative? || @balloon >= @amount_financed raise(ArgumentError, 'at least one rate is required.') if rates.empty? raise(ArgumentError, 'rates must be Finrb::Rate instances.') unless rates.all?(Rate) raise(ArgumentError, 'every rate must have a duration.') if rates.any? { |rate| rate.duration.nil? } @rates = rates @block = block # compute the total duration from all of the rates. @periods = rates.sum(&:duration) valid_interest_only = interest_only_periods.is_a?(Integer) && interest_only_periods.between?(0, @periods - 1) raise(ArgumentError, 'interest_only_periods must be a non-negative integer shorter than the loan term.') unless valid_interest_only @interest_only_periods = interest_only_periods @period = 0 compute end |
Instance Attribute Details
#amount_financed ⇒ Flt::DecNum (readonly)
Returns principal balance including any financed origination fee.
67 68 69 |
# File 'lib/finrb/amortization.rb', line 67 def amount_financed @amount_financed end |
#balance ⇒ Flt::DecNum (readonly)
Returns the balance of the loan at the end of the amortization period (usually zero).
63 64 65 |
# File 'lib/finrb/amortization.rb', line 63 def balance @balance end |
#balloon ⇒ Flt::DecNum (readonly)
Returns contractual principal settled as a balloon in the final period.
65 66 67 |
# File 'lib/finrb/amortization.rb', line 65 def balloon @balloon end |
#finance_origination_fee ⇒ Boolean (readonly) Also known as: finance_origination_fee?
Returns the value of attribute finance_origination_fee.
155 156 157 |
# File 'lib/finrb/amortization.rb', line 155 def finance_origination_fee @finance_origination_fee end |
#interest_only_periods ⇒ Integer (readonly)
Returns number of leading periods that pay interest but no scheduled principal.
73 74 75 |
# File 'lib/finrb/amortization.rb', line 73 def interest_only_periods @interest_only_periods end |
#net_proceeds ⇒ Flt::DecNum (readonly)
Returns cash made available to the borrower after an unfinanced fee.
69 70 71 |
# File 'lib/finrb/amortization.rb', line 69 def net_proceeds @net_proceeds end |
#origination_fee ⇒ Flt::DecNum (readonly)
Returns fee charged when the loan is originated.
71 72 73 |
# File 'lib/finrb/amortization.rb', line 71 def origination_fee @origination_fee end |
#payment ⇒ Flt::DecNum (readonly)
Returns the required monthly payment. For loans with more than one rate, returns nil.
75 76 77 |
# File 'lib/finrb/amortization.rb', line 75 def payment @payment end |
#principal ⇒ Flt::DecNum (readonly)
Returns the principal amount of the loan.
77 78 79 |
# File 'lib/finrb/amortization.rb', line 77 def principal @principal end |
#rates ⇒ Array (readonly)
Returns the interest rates used for calculating the amortization.
79 80 81 |
# File 'lib/finrb/amortization.rb', line 79 def rates @rates end |
Class Method Details
.payment(principal, rate, periods, balloon: 0) ⇒ Flt::DecNum
in most cases, you will probably want to use rate.monthly when calling this function outside of an Amortization instance.
Returns the periodic payment due on a loan.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/finrb/amortization.rb', line 93 def self.payment(principal, rate, periods, balloon: 0) principal = Validation.positive_decimal(principal, name: 'principal', message: 'principal must be positive.') balloon = Validation.decimal(balloon, name: 'balloon') raise(ArgumentError, 'balloon must be non-negative and no greater than principal.') unless balloon.between?(0, principal) rate = Validation.decimal_greater_than(rate, minimum: -1, name: 'periodic rate') periods = Validation.positive_integer(periods, name: 'period count') if rate.zero? # simplified formula to avoid division-by-zero when interest rate is zero -Precision.money((principal - balloon) / periods) else growth = (rate + 1)**periods -Precision.money(((principal * growth) - balloon) * rate / (growth - 1)) end end |
Instance Method Details
#==(other) ⇒ Numeric
compare two Amortization instances
151 152 153 |
# File 'lib/finrb/amortization.rb', line 151 def ==(other) (principal == other.principal) && (origination_fee == other.origination_fee) && (finance_origination_fee? == other.finance_origination_fee?) && (balloon == other.balloon) && (interest_only_periods == other.interest_only_periods) && (rates == other.rates) && (payments == other.payments) end |
#additional_payments ⇒ Array
Returns the amount of any additional payments in each period.
163 164 165 |
# File 'lib/finrb/amortization.rb', line 163 def additional_payments @transactions.filter_map { |trans| trans.difference if trans.payment? } end |
#duration ⇒ Integer
Returns the time required to pay off the loan, in months.
238 239 240 |
# File 'lib/finrb/amortization.rb', line 238 def duration payments.length end |
#inspect ⇒ String
242 243 244 |
# File 'lib/finrb/amortization.rb', line 242 def inspect "Amortization.new(#{@principal})" end |
#interest ⇒ Array
Returns the amount of interest charged in each period.
255 256 257 |
# File 'lib/finrb/amortization.rb', line 255 def interest @transactions.filter_map { |trans| trans.amount if trans.interest? } end |
#payments ⇒ Array
Returns the amount of the payment in each period.
264 265 266 |
# File 'lib/finrb/amortization.rb', line 264 def payments @transactions.filter_map { |trans| trans.amount if trans.payment? } end |