Tuesday, October 14, 2008

Mortgage calculator using SQL Model clause


Recently we had local Latvian Oracle day conference. This year it was quite big event with 5 parallel sessions and more than 400 participants. I also was one of the presenters telling about analytic functions and SQL Model clause. During the process of understanding deeper and better Oracle SQL Model clause I've tried make also some a bit complex examples myself. Nowadays the actual problem is mortgage and of course the possibility to pay monthly payments (fortunately not for me :) ) on the background of world wide financial crisis.
So here is fully functional mortgage calculator using SQL Model clause. There are two variants:I've used this site to get formula for fixed monthly payment required to fully amortize a loan over a term of fixed months at a fixed monthly interest rate.
OK and now the scripts.

set ver off
set lines 120
undefine rem_loan year_int_rate term
SELECT m+1 month,
to_char(rem_loan, '99999999.00') rem_loan,
to_char(loan_paid_tot, '99999999.00') loan_paid_tot,
to_char(mon_int, '999999.00') mon_int,
to_char(tot_int, '99999999.00') tot_int,
to_char(mon_paym, '99999999.00') mon_paym,
to_char(mon_paym_tot, '99999999.00') mon_paym_tot,
to_char(grand_tot, '99999999.00') grand_tot
FROM dual
MODEL
DIMENSION BY (-1 m)
MEASURES (&&rem_loan rem_loan,
round(&&rem_loan*&&year_int_rate/100/12,2) mon_int,
ceil(&&rem_loan/&&term*100)/100 mon_paym,
(&&rem_loan/&&term*100)/100 loan_paid_tot,
round(&&rem_loan*&&year_int_rate/12/100,2) tot_int,
ceil(&&rem_loan/&&term*100)/100 + round(&&rem_loan*&&year_int_rate/100/12,2) mon_paym_tot,
ceil(&&rem_loan/&&term*100)/100 + round(&&rem_loan*&&year_int_rate/100/12,2) grand_tot
)
RULES ITERATE (&&term) UNTIL (round(loan_paid_tot[iteration_number], 2) = &&rem_loan) (
rem_loan[iteration_number] = rem_loan[iteration_number -1] - mon_paym[iteration_number - 1],
mon_int[iteration_number] = round(rem_loan[iteration_number]*&&year_int_rate/100/12,2),
mon_paym[iteration_number] = least(ceil(&&rem_loan/&&term*100)/100, rem_loan[iteration_number]),
loan_paid_tot[iteration_number] = loan_paid_tot[iteration_number - 1] + mon_paym[iteration_number],
tot_int[iteration_number] = tot_int[iteration_number - 1] + mon_int[iteration_number],
mon_paym_tot[iteration_number] = mon_paym[iteration_number] + mon_int[iteration_number],
grand_tot[iteration_number] = grand_tot[iteration_number - 1] + mon_paym_tot[iteration_number]);

Result for loan of 1000 money units (lats, euros, dollars, pounds whatever) for 10 % year rate with term of 7 months looks as follows:

Enter value for rem_loan: 1000
Enter value for year_int_rate: 10
Enter value for term: 7

MONTH REM_LOAN LOAN_PAID_TO MON_INT TOT_INT MON_PAYM MON_PAYM_TOT GRAND_TOT
---------- ------------ ------------ ---------- ------------ ------------ ------------ ------------
0 1000.00 142.86 8.33 8.33 142.86 151.19 151.19
1 857.14 285.72 7.14 15.47 142.86 150.00 301.19
2 714.28 428.58 5.95 21.42 142.86 148.81 450.00
3 571.42 571.44 4.76 26.18 142.86 147.62 597.62
4 428.56 714.30 3.57 29.75 142.86 146.43 744.05
5 285.70 857.16 2.38 32.13 142.86 145.24 889.29
6 142.84 1000.00 1.19 33.32 142.84 144.03 1033.32
For fixed payment each month the script is as follows:

SELECT m+1 month,
to_char(rem_loan, '99999999.00') rem_loan,
to_char(loan_paid_tot, '99999999.00') loan_paid_tot,
to_char(mon_int, '999999.00') mon_int,
to_char(tot_int, '99999999.00') tot_int,
to_char(mon_paym, '99999999.00') mon_paym,
to_char(mon_paym_tot, '99999999.00') mon_paym_tot,
to_char(grand_tot, '99999999.00') grand_tot
FROM dual
MODEL
DIMENSION BY (-1 m)
MEASURES (&&rem_loan rem_loan,
round(&&rem_loan*&&year_int_rate/100/12,2) mon_int,
ceil(&&rem_loan*(&&year_int_rate/100/12*POWER((1+&&year_int_rate/100/12),&&term))/
(POWER((1+&&year_int_rate/100/12),&&term)-1)*100)/100 -
round(&&rem_loan*&&year_int_rate/100/12,2) mon_paym,
ceil(&&rem_loan*(&&year_int_rate/100/12*POWER((1+&&year_int_rate/100/12),&&term))/
(POWER((1+&&year_int_rate/100/12),&&term)-1)*100)/100 -
round(&&rem_loan*&&year_int_rate/100/12,2) loan_paid_tot,
round(&&rem_loan*&&year_int_rate/12/100,2) tot_int,
ceil(&&rem_loan*(&&year_int_rate/100/12*POWER((1+&&year_int_rate/100/12),&&term))/
(POWER((1+&&year_int_rate/100/12),&&term)-1)*100)/100 mon_paym_tot,
ceil(&&rem_loan*(&&year_int_rate/100/12*POWER((1+&&year_int_rate/100/12),&&term))/
(POWER((1+&&year_int_rate/100/12),&&term)-1)*100)/100 grand_tot
)
RULES ITERATE (&&term) UNTIL (round(loan_paid_tot[iteration_number], 2) = &&rem_loan) (
rem_loan[iteration_number] = rem_loan[iteration_number -1] - mon_paym[iteration_number - 1],
mon_int[iteration_number] = round(rem_loan[iteration_number]*&&year_int_rate/100/12,2),
mon_paym_tot[iteration_number] = least(
ceil(&&rem_loan*(&&year_int_rate/100/12*POWER((1+&&year_int_rate/100/12),&&term))/
(POWER((1+&&year_int_rate/100/12),&&term)-1)*100)/100,
rem_loan[iteration_number] + mon_int[iteration_number]),
mon_paym[iteration_number] = mon_paym_tot[iteration_number] - mon_int[iteration_number],
loan_paid_tot[iteration_number] = loan_paid_tot[iteration_number - 1] + mon_paym[iteration_number],
tot_int[iteration_number] = tot_int[iteration_number - 1] + mon_int[iteration_number],
grand_tot[iteration_number] = grand_tot[iteration_number - 1] + mon_paym_tot[iteration_number]);
Result for the same loan of 1000 money units (lats, euros, dollars, pounds whatever) for 10 % year rate with term of 7 months looks as follows:

MONTH REM_LOAN LOAN_PAID_TO MON_INT TOT_INT MON_PAYM MON_PAYM_TOT GRAND_TOT
---------- ------------ ------------ ---------- ------------ ------------ ------------ ------------
0 1000.00 139.33 8.33 8.33 139.33 147.66 147.66
1 860.67 279.82 7.17 15.50 140.49 147.66 295.32
2 720.18 421.48 6.00 21.50 141.66 147.66 442.98
3 578.52 564.32 4.82 26.32 142.84 147.66 590.64
4 435.68 708.35 3.63 29.95 144.03 147.66 738.30
5 291.65 853.58 2.43 32.38 145.23 147.66 885.96
6 146.42 1000.00 1.22 33.60 146.42 147.64 1033.60
During my research I've found invaluable these resources: