lib/finrb/amortization.rb in finrb-0.1.0 vs lib/finrb/amortization.rb in finrb-0.1.1

- old
+ new

@@ -1,10 +1,10 @@ # frozen_string_literal: true -require_relative "cashflows" -require_relative "decimal" -require_relative "transaction" +require_relative 'cashflows' +require_relative 'decimal' +require_relative 'transaction' module Finrb # the Amortization class provides an interface for working with loan amortizations. # @note There are _two_ ways to create an amortization. The first # example uses the amortize method for the Numeric class. The second @@ -32,10 +32,30 @@ attr_reader :principal # @return [Array] the interest rates used for calculating the amortization # @api public attr_reader :rates + # @return [DecNum] the periodic payment due on a loan + # @param [DecNum] principal the initial amount of the loan or investment + # @param [Rate] rate the applicable interest rate (per period) + # @param [Integer] periods the number of periods needed for repayment + # @note in most cases, you will probably want to use rate.monthly when calling this function outside of an Amortization instance. + # @example + # rate = Rate.new(0.0375, :apr, :duration => (30 * 12)) + # rate.duration #=> 360 + # Amortization.payment(200000, rate.monthly, rate.duration) #=> DecNum('-926.23') + # @see https://en.wikipedia.org/wiki/Amortization_calculator + # @api public + def self.payment(principal, rate, periods) + if rate.zero? + # simplified formula to avoid division-by-zero when interest rate is zero + -(principal / periods).round(2) + else + -(principal * (rate + (rate / (((rate + 1)**periods) - 1)))).round(2) + end + end + # create a new Amortization instance # @return [Amortization] # @param [DecNum] principal the initial amount of the loan or investment # @param [Rate] rates the applicable interest rates # @param [Proc] block @@ -52,11 +72,11 @@ compute end # compare two Amortization instances # @return [Numeric] -1, 0, or +1 - # @param [Amortization] + # @param [Amortization] other # @api public def ==(other) (principal == other.principal) && (rates == other.rates) && (payments == other.payments) end @@ -154,29 +174,9 @@ # amt = 300000.amortize(rate) # amt.interest[0,6].sum #=> DecNum('5603.74') # @api public def interest @transactions.filter_map { |trans| trans.amount if trans.interest? } - end - - # @return [DecNum] the periodic payment due on a loan - # @param [DecNum] principal the initial amount of the loan or investment - # @param [Rate] rate the applicable interest rate (per period) - # @param [Integer] periods the number of periods needed for repayment - # @note in most cases, you will probably want to use rate.monthly when calling this function outside of an Amortization instance. - # @example - # rate = Rate.new(0.0375, :apr, :duration => (30 * 12)) - # rate.duration #=> 360 - # Amortization.payment(200000, rate.monthly, rate.duration) #=> DecNum('-926.23') - # @see https://en.wikipedia.org/wiki/Amortization_calculator - # @api public - def self.payment(principal, rate, periods) - if rate.zero? - # simplified formula to avoid division-by-zero when interest rate is zero - -(principal / periods).round(2) - else - -(principal * (rate + (rate / (((1 + rate)**periods) - 1)))).round(2) - end end # @return [Array] the amount of the payment in each period # @example find the total payments for a loan # rate = Rate.new(0.0375, :apr, :duration => (30 * 12))