HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Calculation of mortgage repayments

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
calculationrepaymentsmortgage

Problem

I've just started learning R, but I feel that I'm using it only as a normal procedural language without taking advantage of some built-in functions and data structures.

I wrote a function that calculates repayments for mortgage with an option of specifying list of overpayments to reduce monthly payments. Please suggest any improvements on making it more native to R.

source("http://pastebin.com/raw.php?i=q7tyiEmM") ## for pmt()
mortgage  0 && nrow(overPayments) > 0) {
            overPayments <- sum(overPayments$payment)
            currentCapital <- currentCapital - overPayments
            payment <- -pmt(monthlyRate, nper = months + 1 - currentMonth, pv = currentCapital)
        }

        capital <- append(capital, currentCapital)

        totalPaid <- totalPaid + payment
        payments <- append(payments, payment)
    }
    cat("Would have paid: ", round(totalShouldPay, 2), "\n")
    cat("Will pay: ", round(totalPaid, 2), "\n")
    cat("Saved: ", round(totalShouldPay - totalPaid, 2), "\n")
    return(data.frame(capital=capital, payments=payments))
}

overPayments <- data.frame(
    month   = c(20,    25,    30,    35,    40), 
    payment = c(20000, 20000, 20000, 20000, 20000))

cap <- mortgage(
    over = overPayments
)

#print(cap)
par(mfcol=c(1, 2))
plot(cap$payments, type="s", main = "Payments", xlab = "Month", ylab = "Payment")
plot(cap$capital, type="l", main = "Capital", xlab = "Month", ylab = "Remaining loan")

Solution

I spent clearly too much time on this... Main ideas:

  • your understanding of how the payment on a fixed mortgage is computed was wrong. The payment is computed once at the beginning and remains the same regardless of prepayments, which is why people who choose to prepay end up repaying their whole mortgage before the scheduled term, as the example below should show.



  • I added pmt as an argument to the function as it is best practice for a function to not use any variable defined outside its scope (the dreaded global variables)



  • A big no-no in your code was the use of constructs like capital



  • You'll see I use things like 12L instead of 12. That's the difference between integers and numerics. It is recommended to use integers when possible as it can prevent (rare) issues regarding floating point errors. It uses less memory and can sometimes make your code faster. Again, you won't notice it here, but it is a good practice.



  • Regarding @janos' comment about trying to avoid for loops. I agree, R works best and faster when vectorization can replace loops. However, your particular algorithm is a case of an iterative (state i+1 depends on state i) process so it cannot be vectorized. Whereas vectorization can replace loops only in processes where each iteration can be computed independently. So you will find that my answer still has a loop, in the form of a while()`.



  • I modified the variables' names to be closer to the official language. (I work in finance.)



mortgage  0) {

      balance.due       <- start.balance * (1 + monthly.rate)
      payment.due       <- min(monthly.payment, balance.due)
      prepayment        <- if (is.null(prepayments)) 0 else
                           min(sum(subset(prepayments, month == month.idx)$payment),
                               balance.due - payment.due)
      total.payment     <- payment.due + prepayment
      interest.payment  <- start.balance * monthly.rate
      end.balance       <- balance.due - total.payment
      principal.payment <- start.balance - end.balance

      db[[month.idx]] <- c(month             = month.idx,
                           start.balance     = start.balance,
                           balance.due       = balance.due,
                           payment.due       = payment.due,
                           total.payment     = total.payment,
                           prepayment        = prepayment,
                           interest.payment  = interest.payment,
                           principal.payment = principal.payment,
                           end.balance       = end.balance)
      month.idx     <- month.idx + 1L
      start.balance <- end.balance
   }
   as.data.frame(do.call(rbind, db))
}

source("http://pastebin.com/raw.php?i=q7tyiEmM")  # for pmt()

overPayments <- data.frame(
   month   = c(20,    25,    30,    35,    40), 
   payment = c(20000, 20000, 20000, 20000, 20000))

cap1 <- mortgage()
cap2 <- mortgage(prepayments = overPayments)

par(mfcol=c(1, 2))
plot(c(cap1$start.balance, 0), type="l",
     main = "Without Prepayments", xlab = "Month", ylab = "Balance")
plot(c(cap2$start.balance, 0), type="l",
     main = "With Prepayments",    xlab = "Month", ylab = "Balance")

Code Snippets

mortgage <- function(property.value = 1e6,
                     downpayment = 0.10,
                     interest.rate = 0.04,
                     term = 25L,
                     prepayments = NULL,
                     pmt.fun = pmt) {

   original.balance <- property.value * (1 - downpayment)
   monthly.rate     <- interest.rate / 12L
   term.in.months   <- term * 12L
   monthly.payment  <- -pmt.fun(monthly.rate, nper = term.in.months,
                                              pv = original.balance) 
   db <- list()
   month.idx <- 1L
   start.balance <- original.balance

   while(start.balance > 0) {

      balance.due       <- start.balance * (1 + monthly.rate)
      payment.due       <- min(monthly.payment, balance.due)
      prepayment        <- if (is.null(prepayments)) 0 else
                           min(sum(subset(prepayments, month == month.idx)$payment),
                               balance.due - payment.due)
      total.payment     <- payment.due + prepayment
      interest.payment  <- start.balance * monthly.rate
      end.balance       <- balance.due - total.payment
      principal.payment <- start.balance - end.balance

      db[[month.idx]] <- c(month             = month.idx,
                           start.balance     = start.balance,
                           balance.due       = balance.due,
                           payment.due       = payment.due,
                           total.payment     = total.payment,
                           prepayment        = prepayment,
                           interest.payment  = interest.payment,
                           principal.payment = principal.payment,
                           end.balance       = end.balance)
      month.idx     <- month.idx + 1L
      start.balance <- end.balance
   }
   as.data.frame(do.call(rbind, db))
}

source("http://pastebin.com/raw.php?i=q7tyiEmM")  # for pmt()

overPayments <- data.frame(
   month   = c(20,    25,    30,    35,    40), 
   payment = c(20000, 20000, 20000, 20000, 20000))

cap1 <- mortgage()
cap2 <- mortgage(prepayments = overPayments)

par(mfcol=c(1, 2))
plot(c(cap1$start.balance, 0), type="l",
     main = "Without Prepayments", xlab = "Month", ylab = "Balance")
plot(c(cap2$start.balance, 0), type="l",
     main = "With Prepayments",    xlab = "Month", ylab = "Balance")

Context

StackExchange Code Review Q#60097, answer score: 6

Revisions (0)

No revisions yet.