Skip to content

Commit 012ea3c

Browse files
authored
Merge pull request #8 from wowinter13/feat/loan_fv
Implement Finance::Loan#fv
2 parents 22b885e + e799fbf commit 012ea3c

File tree

3 files changed

+74
-2
lines changed

3 files changed

+74
-2
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ which are as follows:
1111

1212
| numpy-financial function | ruby native function ported? | info|
1313
|:------------------------: |:------------------: | :------------------|
14-
| fv | | Computes the future value|
14+
| fv | | Computes the future value|
1515
| ipmt | | Computes interest payment for a loan|
1616
| pmt || Computes the fixed periodic payment(principal + interest) made against a loan amount|
1717
| ppmt | | Computes principal payment for a loan|

lib/finance/loan.rb

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ class Loan
55
PAYMENT_TYPE_MAPPING = { end: 0, beginning: 1 }.freeze
66

77
# @return [Float] The amount of loan request (I.e. a present value)
8+
# You can use #pv method to calculate value if param is not defined.
89
# Defaults to 0.
910
attr_accessor :amount
1011

@@ -27,23 +28,32 @@ class Loan
2728
attr_accessor :duration
2829

2930
# @return [Float] Future value.
31+
# You can use #fv method to calculate value if param is not defined.
3032
# Defaults to 0.
3133
attr_accessor :future_value
3234

35+
# @return [Float] The (fixed) periodic payment.
36+
# You can use #pmt method to calculate value if param is not defined.
37+
attr_accessor :payment
38+
39+
# Create a new Loan instance.
3340
def initialize(**options)
3441
initialize_payment_type(options[:ptype])
3542
@nominal_rate = options.fetch(:nominal_rate, 0).to_f
3643
@duration = options.fetch(:duration, 1).to_f
3744
@amount = options.fetch(:amount, 0).to_f
3845
@future_value = options.fetch(:future_value, 0).to_f
39-
@monthly_rate = @nominal_rate / 12
46+
@payment = options[:payment]
47+
@monthly_rate = @nominal_rate / 12
4048
end
4149

4250
# Pmt computes the payment against a loan principal plus interest (future_value = 0).
4351
# It can also be used to calculate the recurring payments needed to achieve
4452
# a certain future value given an initial deposit,
4553
# a fixed periodically compounded interest rate, and the total number of periods.
4654
#
55+
# Required Loan arguments: nominal_rate, duration, amount, future_value*
56+
#
4757
# @return [Numeric] The (fixed) periodic payment.
4858
#
4959
# @example
@@ -69,6 +79,36 @@ def pmt
6979
(-future_value + amount * factor) / second_factor
7080
end
7181

82+
# Fv computes future value at the end of some periods (duration).
83+
# Required Loan arguments: nominal_rate, duration, payment, amount*
84+
#
85+
# @param payment [Float] The (fixed) periodic payment.
86+
# In case you don't want to modify the original loan, use this parameter to recalculate fv.
87+
#
88+
# @return [Float] The value at the end of the `duration` periods.
89+
#
90+
# @example
91+
# require 'finance_rb'
92+
# Finance::Loan.new(nominal_rate: 0.05, duration: 120, amount: -100, payment: -200).fv
93+
# #=> 15692.928894335748
94+
#
95+
# @see http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formulaOpenDocument-formula-20090508.odt
96+
# @see [WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
97+
# Open Document Format for Office Applications (OpenDocument)v1.2,
98+
# Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
99+
# Pre-Draft 12. Organization for the Advancement of Structured Information
100+
# Standards (OASIS). Billerica, MA, USA. [ODT Document].
101+
def fv(payment: nil)
102+
raise ArgumentError, 'no payment given' if self.payment.nil? && payment.nil?
103+
104+
final_payment = payment || self.payment
105+
106+
factor = (1.0 + monthly_rate)**duration
107+
second_factor = (factor - 1) * (1 + monthly_rate * ptype) / monthly_rate
108+
109+
-((amount * factor) + (final_payment.to_f * second_factor))
110+
end
111+
72112
private
73113

74114
def initialize_payment_type(ptype)

spec/finance/loan_spec.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,36 @@
3838
expect(loan.pmt).to eq(17_449.90775727763)
3939
end
4040
end
41+
42+
describe '#fv' do
43+
context 'with loan arguments' do
44+
it 'calculates correct fv value' do
45+
loan = Finance::Loan.new(nominal_rate: 0.05, duration: 120, amount: -100, payment: -100)
46+
expect(loan.fv).to eq(15_692.928894335748)
47+
end
48+
49+
context 'with :ptype' do
50+
it 'calculates correct fv value' do
51+
loan = Finance::Loan.new(
52+
nominal_rate: 0.9, duration: 20, amount: 0, payment: -2000, ptype: :beginning
53+
)
54+
expect(loan.fv).to eq(93_105.06487352113)
55+
end
56+
end
57+
end
58+
59+
context 'with an optional :payment argument' do
60+
it 'calculates correct fv value' do
61+
loan = Finance::Loan.new(nominal_rate: 0.05, duration: 120, amount: -100, payment: -200)
62+
expect(loan.fv(payment: -100)).to eq(15_692.928894335748)
63+
end
64+
end
65+
66+
context 'w/o any payments' do
67+
it 'raises an ArgumentError exception w/o loan arguments' do
68+
loan = Finance::Loan.new(nominal_rate: 0.05, duration: 120, amount: -100)
69+
expect { loan.fv }.to raise_error(ArgumentError, "no payment given")
70+
end
71+
end
72+
end
4173
end

0 commit comments

Comments
 (0)