class FinanceMath::Loan
the Loan
class provides an interface for working with interest rates. @api public
Attributes
@return [Float] the amount of loan request @api public
@return [DecNum] the currency protection @api public
@return [Integer] the duration for which the rate is valid, in months @api public
@return [Float] fee @api public
@return [DecNum] the monthly rate @api public
@return [Float] the nominal annual rate @api public
@return [DecNum] P principal @api public
@return [DecNum] the fee for the bank/market @api public
Public Class Methods
create a new Loan
instance @return [Loan] @param [Numeric] decimal value of the interest rate @param [Integer] Duration of the loan period @param [Float] Loan
amount @param [Float] structure fee - fee for the market in percentages @param [Float] currency protection - Protection for currency changes - usually 3%, default to 0% @example create a 10.5% Nominal rate @see en.wikipedia.org/wiki/Nominal_interest_rate @api public
# File lib/finance_math/loan.rb, line 48 def initialize(options = {}) initialize_options(options) @principal = principal_calculation @monthly_rate = @nominal_rate / 100 / 12 end
Public Instance Methods
# File lib/finance_math/loan.rb, line 60 def apr @apr ||= calculate_apr end
# File lib/finance_math/loan.rb, line 54 def pmt(options = {}) future_value = options.fetch(:future_value, 0) type = options.fetch(:type, 0) ((@amount * interest(@monthly_rate, @duration) - future_value ) / ((1.0 + @monthly_rate * type) * fvifa(@monthly_rate, duration))) end
Protected Instance Methods
# File lib/finance_math/loan.rb, line 78 def fvifa(monthly_rate, duration) (monthly_rate == 0) ? duration : pow1pm1(monthly_rate, duration) / monthly_rate end
# File lib/finance_math/loan.rb, line 74 def interest(monthly_rate, duration) pow1p(monthly_rate, duration) end
# File lib/finance_math/loan.rb, line 70 def pow1p(x, y) (x.abs > 0.5) ? ((1 + x) ** y) : Math.exp(y * Math.log(1.0 + x)) end
# File lib/finance_math/loan.rb, line 66 def pow1pm1(x, y) (x <= -1) ? ((1 + x) ** y) - 1 : Math.exp(y * Math.log(1.0 + x)) - 1 end
Private Instance Methods
solves APR
- a (1 + a)^N
-
/ [(1 + a)^N - 1] - P/C = 0
where a = APR/1200, N = duration, P = monthly payment, C = loan_amount Newton-Raphson finds root (the value for 'a' that makes f(a) = 0)
# File lib/finance_math/loan.rb, line 101 def calculate_apr payment_ratio = pmt / principal_calculation duration = @duration f = lambda {|k| (k**(duration + 1) - (k**duration * (payment_ratio + 1)) + payment_ratio)} f_deriv = lambda { |k| ((duration + 1) * k**duration) - (duration * (payment_ratio + 1) * k**(duration - 1))} root = newton_raphson(f, f_deriv, monthly_rate + 1) 100 * 12 * (root -1).to_f end
# File lib/finance_math/loan.rb, line 84 def initialize_options(options) @nominal_rate = options.fetch(:nominal_rate).to_f @duration = options.fetch(:duration).to_f @amount = options.fetch(:amount).to_f @structure_fee = options.fetch(:structure_fee, 5).to_f @currency_protection = options.fetch(:currency_protection, 3).to_f @fee = options.fetch(:fee, 0).to_f end
'start' is the monthly_rate
, Newton Raphson will find the apr root very quickly k1 = k0 - f(k0)/f'(k0) k_plus_one = k - f(k)/f_deriv(k) f_deriv should be an positive number! We find the k-intercept of the tangent line at point k_plus_one and compare k to k_plus_one. This is repeated until a sufficiently accurate value is reached, which can be specified with the 'precision' parameter
# File lib/finance_math/loan.rb, line 116 def newton_raphson(f, f_deriv, start, precision = 5) k_plus_one = start k = 0.0 while ((k - 1) * 10**precision).to_f.floor != ((k_plus_one - 1) * 10**precision).to_f.floor k = k_plus_one k_plus_one = k - f.call(k) / f_deriv.call(k).abs end k_plus_one end
# File lib/finance_math/loan.rb, line 93 def principal_calculation amount * (1 - currency_protection/100 - structure_fee / 100 ) - fee * duration end