loan repayment functions

Phil Frost indigo at bitglue.com
Wed Jan 12 22:53:53 EST 2011


I was unable to create a SX for a loan repayment because I had in the
past made some additional payments towards principal. My monthly
payment remains the same, so the split between principal and interest
can't be calculated (to my knowledge) with the existing pmt, ppmt, ipmt
functions.

My solution was to implement the following functions in fin.scm. The
bulk of the work is loan_balance, which, given an original principal
amount, interest rate, and monthly payment, calculates the principal
balance after having made N such payments. Convienence functions are
implemented to calculate the interest and principal parts of each
payment.

My implementation also calculates the balance by iterating through each
payment cycle, rounding the interest to whole cents each time. At least
for my bank, I was thus able to get it to correctly calculate each
payment of my 30 year loan to the penny. (except for another issue,
described below)

In developing these functions I observed a few suprising behaviours of
my 2.2.9 linux installation:

Firstly, I noticed the SX editor seems to call the functions with an
aparently random value for "i" when the "OK" button is clicked. Since my
function iterates through "i" payments, and odds are "i" is very large,
this usually resulted in an aparent hang, as it attempted to calculate
the (negative) principal balance after two billion payments. I changed
my implementation to terminate after the principal balance reaches zero,
but I suspect the random "i" value is a hint of a problem elsewhere.

Also, there seems to be some loss of precision under some conditions
between the SX editor and the call to the scheme function. I (through
experimentation) determined that my bank rounds my monthly interest rate
of 0.065/12 to 0.005416667. So, I had the formula in the SX editor like
so:

loan_principal( 102392.64 : 0.005416667 : 675.19 : i )

however, somehow 0.005416667 becomes 0.00541666 by the time my function
is called. If I specify "0.065/12" in the SX editor, the function is
called with a much more precise value.

Anyway, here are the functions:

(define (round-cents n) (* 0.01 (round (* 100 n))))

; calculate the balance on a loan, after having made n payments
; principal: original principal of the loan. When called with n=0, returns this value
; rate: the per-period interest rate
; payment: the amount that is paid each month
(define (gnc:loan_balance principal rate payment n)
  (letrec ((P (lambda (n p)
    ; n: the number of payments left to apply
    ; p: the outstanding balance prior to the payment being made
    (cond
      ((<= n 0) p)
      ((<= p 0) 0)
      (else (P (- n 1) (- p (- payment (round-cents (* p rate))))))))))
  (P n principal)))

;; calculate the interest or principal that is due after having made n payments.
;; That is, due on the (n+1)th payment.
(define (gnc:loan_interest principal rate payment n)
  (round-cents (* rate (gnc:loan_balance principal rate payment n))))

(define (gnc:loan_principal principal rate payment n)
  (- payment (gnc:loan_interest principal rate payment n)))

Derek Atkins made the suggestion that the rounding precision should not
be fixed, but rather found from the currency or the account. Doing so is
beyond my knowledge of the gnucash API, so I leave that to someone else.


More information about the gnucash-devel mailing list