Skip to content

Instantly share code, notes, and snippets.

@bomatson
Created June 14, 2017 04:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bomatson/eefe4ead1ac3720a2c0c0e264ddc4573 to your computer and use it in GitHub Desktop.
Save bomatson/eefe4ead1ac3720a2c0c0e264ddc4573 to your computer and use it in GitHub Desktop.
defmodule LoanSchedule do
def perform(apr, monthly_payment, balance) do
schedule = calculate(apr, monthly_payment, balance, 0, %{})
total_payments = Map.keys(schedule) |> Enum.max
total_paid = Map.values(schedule) |> Enum.reduce(0, fn(loan, acc) -> loan[:monthly_payment] + acc end)
[
payment_schedule: schedule,
loan_details: %{
apr: apr,
monthly_payment: monthly_payment,
initial_balance: balance,
total_payments: total_payments,
total_paid: total_paid
}
]
end
def calculate(apr, monthly_payment, balance, payments, schedule_map) when balance < monthly_payment do
IO.puts "We are there! Last payment of: #{balance}"
IO.puts "We've made #{payments + 1} payments"
monthly_interest_payment = ((apr / 12) * balance)
current_payment_period = payments + 1
final_payment = balance + monthly_interest_payment
total_paid = (payments * monthly_payment) + final_payment
current_period = [
current_balance: 0,
total_paid: total_paid,
principal_payment: balance,
interest_payment: monthly_interest_payment,
current_payment_period: current_payment_period,
monthly_payment: final_payment
]
Map.put(schedule_map, current_payment_period, current_period)
end
def calculate(apr, monthly_payment, balance, payments, schedule_map) do
%{interest: interest_payment, principal: principal_payment} = current_payments(apr, balance, monthly_payment)
if principal_payment < 0 do
raise "You can't pay this loan. Your minimum payment must be higher than #{interest_payment}"
end
if principal_payment == 0 do
raise "You are paying just below the minimum payment"
end
current_payment_period = payments + 1
next_month_balance = balance - principal_payment
total_paid = current_payment_period * monthly_payment
current_period = [
apr: apr,
current_balance: next_month_balance,
total_paid: total_paid,
principal_payment: principal_payment,
interest_payment: interest_payment,
current_payment_period: current_payment_period,
monthly_payment: monthly_payment
]
new_payment_schedule = Map.put(schedule_map, current_payment_period, current_period)
calculate(apr, monthly_payment, next_month_balance, current_payment_period, new_payment_schedule)
end
def current_payments(apr, balance, monthly_payment) do
monthly_interest_payment = ((apr / 12) * balance)
monthly_principal_payment = monthly_payment - monthly_interest_payment
%{
interest: monthly_interest_payment,
principal: monthly_principal_payment
}
end
end
@bomatson
Copy link
Author

An example use case, with a $10,000 loan, monthly payments of $1,000 and 5% APR:

iex(1)> LoanSchedule.perform(0.05, 1000, 10_000)

We are there! Last payment of: 234.96800783042113
We've made 11 payments
[payment_schedule: %{1 => [apr: 0.05, current_balance: 9041.666666666666,
    total_paid: 1000, principal_payment: 958.3333333333334,
    interest_payment: 41.666666666666664, current_payment_period: 1,
    monthly_payment: 1000],
   2 => [apr: 0.05, current_balance: 8079.340277777777, total_paid: 2000,
    principal_payment: 962.3263888888889, interest_payment: 37.67361111111111,
    current_payment_period: 2, monthly_payment: 1000],
   3 => [apr: 0.05, current_balance: 7113.004195601851, total_paid: 3000,
    principal_payment: 966.336082175926, interest_payment: 33.66391782407407,
    current_payment_period: 3, monthly_payment: 1000],
   4 => [apr: 0.05, current_balance: 6142.641713083525, total_paid: 4000,
    principal_payment: 970.3624825183256, interest_payment: 29.63751748167438,
    current_payment_period: 4, monthly_payment: 1000],
   5 => [apr: 0.05, current_balance: 5168.236053554707, total_paid: 5000,
    principal_payment: 974.4056595288187, interest_payment: 25.594340471181354,
    current_payment_period: 5, monthly_payment: 1000],
   6 => [apr: 0.05, current_balance: 4189.770370444518, total_paid: 6000,
    principal_payment: 978.4656831101887, interest_payment: 21.53431688981128,
    current_payment_period: 6, monthly_payment: 1000],
   7 => [apr: 0.05, current_balance: 3207.2277469880373, total_paid: 7000,
    principal_payment: 982.5426234564811, interest_payment: 17.457376543518826,
    current_payment_period: 7, monthly_payment: 1000],
   8 => [apr: 0.05, current_balance: 2220.591195933821, total_paid: 8000,
    principal_payment: 986.6365510542165, interest_payment: 13.363448945783489,
    current_payment_period: 8, monthly_payment: 1000],
   9 => [apr: 0.05, current_balance: 1229.8436592502119, total_paid: 9000,
    principal_payment: 990.747536683609, interest_payment: 9.252463316390921,
    current_payment_period: 9, monthly_payment: 1000],
   10 => [apr: 0.05, current_balance: 234.96800783042113, total_paid: 10000,
    principal_payment: 994.8756514197908, interest_payment: 5.124348580209216,
    current_payment_period: 10, monthly_payment: 1000],
   11 => [current_balance: 0, total_paid: 10235.947041196381,
    principal_payment: 234.96800783042113, interest_payment: 0.979033365960088,
    current_payment_period: 11, monthly_payment: 235.9470411963812]},
 loan_details: %{apr: 0.05, initial_balance: 10000, monthly_payment: 1000,
   total_paid: 10235.947041196381, total_payments: 11}]

Here, the loan_details provide an actual total_paid value of how much that loan actually costs over time.

The payment_schedule map describes each payment record that is made each month, as well as how much each payment contributed to interest vs principal on the loan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment