class Finance::Loan

Constants

PAYMENT_TYPE_MAPPING

Attributes

amount[RW]

@return [Float] The amount of loan request (I.e. a present value)

You can use #pv method to calculate value if param is not defined.
Defaults to 0
duration[RW]

@return [Float] The number of periods to be compounded for. (I.e. Nper())

Defaults to 1.
You can use #nper method to calculate value if param is not defined.
future_value[RW]

@return [Float] Future value.

You can use #fv method to calculate value if param is not defined.
Defaults to 0
monthly_rate[R]

@return [Float] The monthly rate is the nominal annual rate divided by 12.

Defaults to 0
nominal_rate[RW]

@return [Float] The nominal annual rate of interest as decimal (not per cent).

(e.g., 13% -> 0.13)
Defaults to 0.
payment[RW]

@return [Float] The (fixed) periodic payment.

You can use #pmt method to calculate value if param is not defined.
period[RW]

@return [Float] Period under consideration.

ptype[RW]

@return [Integer] Specification of whether payment is made

at the beginning (ptype = 1) or the end (ptype = 0) of each period.
Defaults to {:end, 0}.

Public Class Methods

new(**options) click to toggle source

Create a new Loan instance.

# File lib/finance/loan.rb, line 44
def initialize(**options)
  initialize_payment_type(options[:ptype])
  @nominal_rate  = options.fetch(:nominal_rate, 0).to_f
  @duration      = options.fetch(:duration, 1).to_f
  @amount        = options.fetch(:amount, 0).to_f
  @future_value  = options.fetch(:future_value, 0).to_f
  @period        = options[:period]
  @payment       = options[:payment]
  @monthly_rate  = @nominal_rate / 12
end

Public Instance Methods

fv(payment: nil) click to toggle source

Fv computes future value at the end of some periods (duration).

Required Loan arguments: nominal_rate, duration, payment, amount*

@param payment [Float] The (fixed) periodic payment.

In case you don't want to modify the original loan, use this parameter to recalculate fv.

@return [Float] The value at the end of the `duration` periods.

@example

require 'finance_rb'
Finance::Loan.new(nominal_rate: 0.05, duration: 120, amount: -100, payment: -200).fv
#=> 15692.928894335748

@see www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).

Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
# File lib/finance/loan.rb, line 171
def fv(payment: nil)
  raise ArgumentError, 'no payment given' if self.payment.nil? && payment.nil?

  final_payment = payment || self.payment

  factor = (1.0 + monthly_rate)**duration
  second_factor = (factor - 1) * (1 + monthly_rate * ptype) / monthly_rate

  -((amount * factor) + (final_payment.to_f * second_factor))
end
ipmt() click to toggle source

IPmt computes interest payment for a loan under a given period.

Required Loan arguments: period, nominal_rate, duration, amount, future_value*

@return [Float] The interest payment for a loan.

@example

require 'finance_rb'
Finance::Loan.new(nominal_rate: 0.0824, duration: 12, amount: 2500, period: 1).ipmt
#=> -17.166666666666668

@see www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).

Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
# File lib/finance/loan.rb, line 104
def ipmt
  raise ArgumentError, 'no period given' if period.nil?

  ipmt_val = remaining_balance * monthly_rate
  if ptype == PAYMENT_TYPE_MAPPING[:beginning]
    period == 1 ? 0.0 : (ipmt_val / 1 + monthly_rate)
  else
    ipmt_val
  end
end
nper() click to toggle source

Nper computes the number of periodic payments.

Required Loan arguments: payment, nominal_rate, period, amount, future_value*

@return [Float] The number of periodic payments.

@example

require 'finance_rb'
Finance::Loan.new(nominal_rate: 0.07, amount: 8000, payment: -150).nper
#=> 64.0733487706618586
# File lib/finance/loan.rb, line 146
def nper
  z = payment * (1.0 + monthly_rate * ptype) / monthly_rate

  Math.log(-future_value + z / (amount + z)) / Math.log(1.0 + monthly_rate)
end
pmt() click to toggle source

Pmt computes the payment against a loan principal plus interest (future_value = 0).

It can also be used to calculate the recurring payments needed to achieve
a certain future value given an initial deposit,
a fixed periodically compounded interest rate, and the total number of periods.

Required Loan arguments: nominal_rate, duration, amount, future_value*

@return [Numeric] The (fixed) periodic payment.

@example

require 'finance_rb'
Finance::Loan.new(nominal_rate: 0.1, duration: 12, amount: 1000, ptype: :end).pmt
#=> 87.9158872300099

@see www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).

Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
# File lib/finance/loan.rb, line 75
def pmt
  factor = (1.0 + monthly_rate)**duration
  second_factor =
    if monthly_rate.zero?
      duration
    else
      (factor - 1) * (1 + monthly_rate * ptype) / monthly_rate
    end

  -((future_value + amount * factor) / second_factor)
end
ppmt() click to toggle source

PPmt computes principal payment for a loan under a given period.

Required Loan arguments: period, nominal_rate, duration, amount, future_value*

@return [Float] The principal payment for a loan under a given period.

@example

require 'finance_rb'
Finance::Loan.new(nominal_rate: 0.0824, duration: 12, amount: 2500, period: 1).ppmt
#=> -200.58192368678277

@see www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).

Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
# File lib/finance/loan.rb, line 132
def ppmt
  pmt - ipmt
end
pv() click to toggle source

Pv computes present value.

Required Loan arguments: nominal_rate, duration, payment, future_value, *ptype

@return [Float] The present value.

@example

require 'finance_rb'
Finance::Loan.new(nominal_rate: 0.24, duration: 12, future_value: 1000, payment: -300, ptype: :ending).pv
#=> 2384.1091906935

@see www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).

Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
# File lib/finance/loan.rb, line 198
def pv
  factor = (1.0 + monthly_rate)**duration
  second_factor = (factor - 1) * (1 + monthly_rate * ptype) / monthly_rate

  -(future_value + (payment.to_f * second_factor)) / factor
end
rate(tolerance: 1e-6, iterations: 100, initial_guess: 0.1) click to toggle source

Rate computes the interest rate per period

by running Newton Rapson to find an approximate value.

@return [Float] The interest rate.

@param tolerance [Float] Required tolerance for the solution. @param initial_guess [Float] Starting guess for solving the rate of interest. @param iterations [Integer] Maximum iterations in finding the solution.

@example

require 'finance_rb'
Finance::Loan.new(nominal_rate: 10, amount: -3500, payment: 0, duration: 10, future_value: 10000).rate
#=> 0.11069085371426901

@see www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).

Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
# File lib/finance/loan.rb, line 225
def rate(tolerance: 1e-6, iterations: 100, initial_guess: 0.1)
  next_iteration_rate = nil
  current_iteration_rate = initial_guess

  (0..iterations).each do |iteration|
    next_iteration_rate = current_iteration_rate - rate_ratio(current_iteration_rate)
    break if (next_iteration_rate - current_iteration_rate).abs <= tolerance
    current_iteration_rate = next_iteration_rate
  end

  next_iteration_rate
end

Private Instance Methods

initialize_payment_type(ptype) click to toggle source

@api private

# File lib/finance/loan.rb, line 258
def initialize_payment_type(ptype)
  @ptype =
    if ptype.nil? || !PAYMENT_TYPE_MAPPING.keys.include?(ptype)
      PAYMENT_TYPE_MAPPING[:end]
    else
      PAYMENT_TYPE_MAPPING[ptype]
    end
end
rate_ratio(rate) click to toggle source

rate_ratio computes the ratio

that is used to find a single value that sets the non-liner equation to zero.

@api private

# File lib/finance/loan.rb, line 244
def rate_ratio(rate)
  t1 = (rate+1.0) ** duration
  t2 = (rate+1.0) ** (duration-1.0)
  g = future_value + t1 * amount + payment * (t1 - 1.0) * (rate * ptype + 1.0) / rate
  derivative_g = \
    (duration * t2 * amount)
      - (payment * (t1 - 1.0) * (rate * ptype + 1.0) / (rate ** 2.0))
      + (duration * payment * t2 * (rate * ptype + 1.0) / rate)
      + (payment * (t1 - 1.0) * ptype/rate)
  
  g / derivative_g
end
remaining_balance() click to toggle source

@api private

# File lib/finance/loan.rb, line 268
def remaining_balance
  self.class.new(
    nominal_rate: nominal_rate.to_f, duration: period - 1.0,
    amount: amount.to_f, ptype: PAYMENT_TYPE_MAPPING.key(ptype)
  ).fv(payment: pmt)
end