mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Amended commit to address pull-request comments.
This commit is contained in:
parent
3ddd76f8f2
commit
5f9020016a
@ -185,3 +185,171 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; Further options to match what some (several? many?) lenders do (at
|
||||
;; least in Canada):
|
||||
;; The posted interest rate is an annual rate that has a specified
|
||||
;; compounding frequency per year (2 for mortgages in Canada).
|
||||
;; A payment frequency and amortization length are selected (e.g.
|
||||
;; monthly payments for 25 years).
|
||||
;; The posted nominal rate is converted from the specified compounding
|
||||
;; frequency to the equivalent rate at the payment frequency.
|
||||
;; The required payment is calculated.
|
||||
;; The payment is rounded up to the next dollar (or $10 dollars,
|
||||
;; or whatever...)
|
||||
;; Each payment period, interest is calculated on the outstanding
|
||||
;; balance.
|
||||
;; The interest is rounded to the nearest cent and added to the
|
||||
;; balance.
|
||||
;; The payment is subtracted from the balance.
|
||||
;; The final payment will be smaller because all the other payments
|
||||
;; were rounded up.
|
||||
;;
|
||||
;; For the purpose of creating scheduled transactions that properly
|
||||
;; debit a source account while crediting the loan account and the
|
||||
;; interest expense account, the first part (the calculation of the
|
||||
;; required payment) doesn't really matter. You have agreed
|
||||
;; with the lender what the payment terms (interest rate, payment
|
||||
;; frequency, payment amount) will be; you keep paying until the
|
||||
;; balance is zero.
|
||||
;;
|
||||
;; To create the scheduled transactions, we need to build an
|
||||
;; amortization table.
|
||||
;; If it weren't for the rounding of the interest to the nearest cent
|
||||
;; each period, we could calculate the ith row of the amortization
|
||||
;; table directly from the general annuity equation (as is done by
|
||||
;; gnc:ipmt and gnc:ppmt). But to deal with the intermediate
|
||||
;; rounding, the amortization table has to be constructed iteratively
|
||||
;; (as is done by the AMORT worksheet on the TI BA II Plus
|
||||
;; financial calculator).
|
||||
;;
|
||||
;; =================================
|
||||
;; EXAMPLE:
|
||||
;; Say you borrow $100,000 at 5%/yr, compounded semi-annually.
|
||||
;; You amortize the loan over 2 years with 24 monthly payments.
|
||||
;; This calls for payments of $4,384.8418 at the end of each month.
|
||||
;; The lender rounds this up to $4,385.
|
||||
;;
|
||||
;; If you calculate the balance at each period directly using the annuity
|
||||
;; formula (like calc-principal does), and then use the those values to calculate
|
||||
;; the principal and interest paid, the first 10 rows of the amortization table
|
||||
;; look like this (the values are rounded to the nearest cent for _display_, but
|
||||
;; not for calculating the next period):
|
||||
;;
|
||||
;; PERIOD | Open | Interest | Principal | End
|
||||
;; 1 |$100,000.00 | $412.39 | $3,972.61 | $96,027.39
|
||||
;; 2 | $96,027.39 | $396.01 | $3,988.99 | $92,038.40
|
||||
;; 3 | $92,038.40 | $379.56 | $4,005.44 | $88,032.96
|
||||
;; 4 | $88,032.96 | $363.04 | $4,021.96 | $84,011.00
|
||||
;; 5 | $84,011.00 | $346.45 | $4,038.55 | $79,972.45
|
||||
;; 6 | $79,972.45 | $329.80 | $4,055.20 | $75,917.25
|
||||
;; 7 | $75,917.25 | $313.08 | $4,071.92 | $71,845.33
|
||||
;; 8 | $71,845.33 | $296.28 | $4,088.72 | $67,756.61
|
||||
;; 9 | $67,756.61 | $279.43 | $4,105.57 | $63,651.04
|
||||
;; 10 | $63,651.04 | $262.49 | $4,122.51 | $59,528.53
|
||||
;;
|
||||
;; If you calculate each period sequentially (rounding the interest and balance
|
||||
;; at each step), you get:
|
||||
;;
|
||||
;; PERIOD | Open | Interest | Principal | End
|
||||
;; 1 |$100,000.00 | $412.39 | $3,972.61 | $96,027.39
|
||||
;; 2 | $96,027.39 | $396.01 | $3,988.99 | $92,038.40
|
||||
;; 3 | $92,038.40 | $379.56 | $4,005.44 | $88,032.96
|
||||
;; 4 | $88,032.96 | $363.04 | $4,021.96 | $84,011.00
|
||||
;; 5 | $84,011.00 | $346.45 | $4,038.55 | $79,972.45
|
||||
;; 6 | $79,972.45 | $329.80 | $4,055.20 | $75,917.25
|
||||
;; 7 | $75,917.25 | $313.08 | $4,071.92 | $71,845.33
|
||||
;; 8 | $71,845.33 | $296.28 | $4,088.72 | $67,756.61
|
||||
;; 9 | $67,756.61 | $279.42 | $4,105.58 | $63,651.03 <- Different
|
||||
;; 10 | $63,651.03 | $262.49 | $4,122.51 | $59,528.52 <- still $0.01 off
|
||||
;;
|
||||
;; =================================
|
||||
;;
|
||||
;; For the following functions the argument names are:
|
||||
;; py: payment frequency (number of payments per year)
|
||||
;; cy: compounding frequency of the nominal rate (per year)
|
||||
;; iy: nominal annual interest rate
|
||||
;; pv: the present value (opening balance)
|
||||
;; pmt: the size of the periodic payment
|
||||
;; n: the payment period we are asking about (the first payment is n=1)
|
||||
;; places: number of decimal places to round the interest amount to
|
||||
;; at each payment (999 does no rounding)
|
||||
;;
|
||||
;; Note: only ordinary annuities are supported (payments at the end of
|
||||
;; each period, not at the beginning of each period)
|
||||
;;
|
||||
;; Unlike the AMORT worksheet on the BA II Plus, these methods will
|
||||
;; handle the smaller payment (bringing the balance to zero, then
|
||||
;; zeroing future payments)
|
||||
;;
|
||||
;; The present value (pv) must be non-negative. If not, the balance will be
|
||||
;; treated as 0.
|
||||
;; The payment (pmt) can be positive (paying interest, and hopefully
|
||||
;; reducing the balance each payment), or negative (increasing the balance
|
||||
; each payment).
|
||||
;; The payment number (n) must be positive for amort_pmt, amort_ppmt, and
|
||||
;; amort_ipmt. I.e., the first payment is payment 1.
|
||||
;; The payment number (n) must be non-negative for amort_balance. (In this
|
||||
;; case, payment zero is at the _beginning_ of the first period, so
|
||||
;; amort_balance will just be the initial balance.)
|
||||
;; If the above conditions on n are violated, the functions return -1 (#f is
|
||||
;; not used, because it causes gnucash to crash).
|
||||
;;
|
||||
;; A negative interest rate works (if you can find a lender who charges
|
||||
;; negative rates), but negative compounding frequency, or negative payment
|
||||
;; frequency is a bad idea.
|
||||
|
||||
;; Calculate the balance remaining after the nth payment
|
||||
;; (n must be greater than or equal to zero)
|
||||
(define (gnc:amort_pmt py cy iy pv pmt n places)
|
||||
(if (< n 1) -1 ;; Returning #f here causes gnucash to crash on startup
|
||||
(let* ((prevBal (gnc:amort_balance py cy iy pv pmt (- n 1) places))
|
||||
(balBeforePayment
|
||||
(amort_balanceAfterInterest prevBal py cy iy places))
|
||||
(balAfterPayment (amort_balanceAfterPayment balBeforePayment pmt)))
|
||||
(- balBeforePayment balAfterPayment))))
|
||||
|
||||
;; Calculate the amount of the nth payment that is principal
|
||||
;; (n must be greater than zero)
|
||||
(define (gnc:amort_ppmt py cy iy pv pmt n places)
|
||||
(if (< n 1) -1
|
||||
(let* ((prevBal (gnc:amort_balance py cy iy pv pmt (- n 1) places))
|
||||
(bal-after-int (amort_balanceAfterInterest prevBal py cy iy places))
|
||||
(newBal (amort_balanceAfterPayment bal-after-int pmt)))
|
||||
(- prevBal newBal))))
|
||||
|
||||
;; Calculate the amount of the nth payment that is interest
|
||||
;; (n must be greater than zero)
|
||||
(define (gnc:amort_ipmt py cy iy pv pmt n places)
|
||||
(if (< n 1) -1
|
||||
(let* ((prevBal(gnc:amort_balance py cy iy pv pmt (- n 1) places)))
|
||||
(amort_interest prevBal py cy iy places))))
|
||||
|
||||
;; "Private" helper functions:
|
||||
|
||||
;; Calculate the amount of interest on the current balance,
|
||||
;; rounded to the specified number of decimal places
|
||||
(define (amort_interest balance py cy iy places)
|
||||
(roundToPlaces (* balance (gnc:periodic_rate iy py cy)) places)
|
||||
)
|
||||
|
||||
;; Calculate the new balance after applying the interest, but before
|
||||
;; applying the payment
|
||||
(define (amort_balanceAfterInterest prevBalance py cy iy places)
|
||||
(+ prevBalance (amort_interest prevBalance py cy iy places))
|
||||
)
|
||||
|
||||
;; Apply the payment to the balance (after the interest has been
|
||||
;; added), without letting the balance go below zero.
|
||||
(define (amort_balanceAfterPayment balanceBeforePmt pmt)
|
||||
(max 0 (- balanceBeforePmt pmt))
|
||||
)
|
||||
|
||||
;; Round the value to the specified number of decimal places.
|
||||
;; 999 places means no rounding (#f is not used, because only numbers can be
|
||||
;; entered in the scheduled transaction editor)
|
||||
(define (roundToPlaces value places)
|
||||
(if (= places 999) value
|
||||
(/ (round (* value (expt 10 places))) (expt 10 places))
|
||||
)
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user