/** * Copyright (c) Wishnu Prasetya, 2009. */ package Hypotheek; import java.util.*; import static Hypotheek.Util.* ; /** * * @author Wishnu Prasetya */ public class Hypotheek { static final int MAX_LOAN_DURATION = 30 * 12 ; static final int MAX_LOAN = euro2cent(21000000) ; static final int MAX_INTEREST_RATE = 100 * 100 ; // 100% /** * Max. duration of the hypotheek in month. */ int duration = MAX_LOAN_DURATION ; /** * How much money you borrow for this hypotheek. This is in Euro-cent. */ int loan = 100 * 100000 ; // 100 K /** * The annual interest rate on this hypotheek. This is expressed in 0.01 %. * So an interest of 3.33 % would be encoded by 333 integer. */ int interestRate = 100 * 5 ; // 5% /** * From the monthly amount used to pay back the loan we first * deduct the loan's interest. The "rest" is normally paid to * reduce the loan. However, as in the spaar-hypotheek we * can instead "save" this money against the same interest rate * as the rate of the hypotheek. The saved money is used to * pay back the loan at the end of the loaning period. In the * Dutch tax system this gives a certain benefit. * *

With this variable we can specify how much percent of the * "rest" meant above will saved using the spaar-hyp scheme. If * 100%, all will be saved. With 50%, then only half will be saved. * Of course in a spaar-hyp we normally save 100%, though with * this option we can hypothetically * analyze a semi spaar-hyp. * *

Percentage will be specified in 0.01 % units. */ int percentageToSave = 0 ; /** * Your annual income in Euro. */ int income = 100* 20000 ; /** * Your age. */ int age = 30 ; /** * The amount of money you want to pay monthly to payback your hypotheek. * This is the bruto amount. That is the amount BEFORE tax. The netto * amount you pay should be less than this, because of the tax reduction * on the interest you pay. */ int monthlyPayment = 100 * 500 ; /** * The overhead cost of purchasing the house. This includes the 6% * overdrachtsbelastix (transfer tax). The usual estimation of the total * purchase overhead is 10%. Usually this overhead is financed from * the loan itself. While this is ok, the interest over this overhead * is not tax deductible. */ int purchaseCostPercentage = 9 * 100 ; // 9% /** * Your bank may require you to have a life insurance so that if * you die, some sufficient amount of money is paid (by the insurance) * to your family and hence the risk that your family cannot pay * back the rest of the loan is minimized. We will not support a * calculation of this premium because this depends on the market * of insurance, as well as your specific agreement with the bank. * * However assuming you can find out how much your premium will be, * or make an estimate, then it can be included in our calculation. * However, for simplicity we will just assume that this premium * will remain constant. * In general this is not the case. It depends on your age and your * health conditions. But again this is also something that depends on * the insurance market. * * This premium is expressed in monthly premium. * By default this will be set to 0. */ int lifeInsurancePremium = 0 ; /** * The used income-tax system. */ InkomenBelasting incomeTaxSystem = new InkomenBelasting2009() ; /** * Tax regulation concerning so-called Eigen Woning Forfait. */ EigenWoningForfait EWforfait = new EigenWoningForfait2007() ; /** * For profit calculation. Expressed in percent, in 0.01 % units. * So a growth of 2.1 % should be represented by 210. */ int assumedHousePriceIncrRate = 200 ; // 2% growth public Hypotheek() { super() ; } public void setDuration(int duration) { assert duration > 0 : "PRE" ; assert duration <= MAX_LOAN_DURATION : "PRE" ; this.duration = duration; } public void setLoan(int loan) { assert loan > 0 : "PRE" ; assert loan <= MAX_LOAN : "PRE" ; this.loan = loan; } public void setIterest(int interestRate) { assert interestRate >= 0 : "PRE" ; assert interestRate <= MAX_INTEREST_RATE : "PRE" ; this.interestRate = interestRate ; } public void setIncome(int income) { assert income > 0 : "PRE" ; assert income <= MAX_LOAN : "PRE" ; this.income = income; } public void setMonthlyPayment(int monthlyPayment) { assert monthlyPayment > 0 : "PRE" ; assert monthlyPayment <= MAX_LOAN : "PRE" ; this.monthlyPayment = monthlyPayment; } static public int MAX_AGE = 200 ; public void setAge(int age) { assert age >= 0 : "PRE" ; assert age <= MAX_AGE : "PRE" ; this.age = age ; } public void setAssumedHousePriceIncrRate(int rate) { assert rate >= 0 : "PRE" ; assert rate <= MAX_INTEREST_RATE : "PRE" ; assumedHousePriceIncrRate = rate ; } public void setPercentageToSave(int p){ assert p >= 0 : "PRE" ; assert p <= 100 * 100 : "PRE" ; percentageToSave = p ; } public void switchToPureSpaarHypotheek() { percentageToSave = 100 * 100 ; } public void setPurchaseCostPerctage(int pc){ assert pc >= 0 : "PRE" ; assert pc <= 100*100 : "PRE" ; purchaseCostPercentage = pc ; } public void setLifeInsurancePremium(int premium){ assert premium >= 0 : "PRE" ; lifeInsurancePremium = premium ; } /** * To hold some data structures to hold calculation results. */ public class Result { List loanPayBacks = new LinkedList() ; List interestPayments = new LinkedList() ; List remainingLoan = new LinkedList() ; List lifeInsurancePremium = new LinkedList() ; /** * For the spaar-hypotheek scheme. */ List saved = new LinkedList() ; int totSaved = 0 ; /** * This hold the total money we have saved up to the corresponding * month, including the interest on the saved money. */ LinkedList progressTotalSaved = new LinkedList() ; List taxReductions = new LinkedList() ; int remaining = loan ; // int monthlyInterestRate = div(interestRate,12) ; // boolean feasible = false ; // Some overall cummulative statistics int totPaidBruto ; int totPaidNetto ; int totInterest ; int totLoanPaidBack ; int totTaxReduction ; int projectedHouseValue ; int profit ; // Some yearly cummulative statistics } /** * Calculate the monthly interest on the loan of x euro-cent, using the interest rate * of this hypotheek. The interest will be expressed in euro-cent. */ private int calcMonthlyInterest(int x) { int monthlyInterestRate = div(interestRate,12) ; float irate = toFloat(monthlyInterestRate) / 100f ; return toInt(calcPercentage(irate,x)) ; } private boolean isMonthlyPaymentSufficient(int remainingLoan) { int interest = calcMonthlyInterest(remainingLoan) ; //System.out.println(">>> remaining loan = " + remainingLoan) ; //System.out.println(">>> monthly payment = " + monthlyPayment) ; //System.out.println(">>> interest = " + interest) ; return monthlyPayment >= interest + lifeInsurancePremium ; } public int getOverheadCost(){ return toInt(calcPercentage(div(purchaseCostPercentage,100),loan)) ; } /** * Since we assume that there may be some overhead cost when we * purchase a house, and that this cost is paid from the loan, * the actual house-price when it is purchased is then the loan * - this cost. */ public int getHousePurchaseValue(){ return loan - getOverheadCost() ; } public int getEigenWoningforfait() { return EWforfait.calc(getHousePurchaseValue()) ; } public Result calculate() { Result R = new Result() ; boolean feasible = isMonthlyPaymentSufficient(loan) ; if (!feasible) return null ; int t = 1 ; while (R.remaining - R.totSaved>0 && t<=duration) { assert isMonthlyPaymentSufficient(R.remaining) ; int interest = calcMonthlyInterest(R.remaining) ; int loanPayment = monthlyPayment - interest - lifeInsurancePremium ; int toSave = toInt(toFloat(percentageToSave)/10000f * toFloat(loanPayment)) ; loanPayment = loanPayment - toSave ; if (R.remaining < loanPayment + toSave) { loanPayment = R.remaining ; toSave = 0 ; } else if (R.remaining - R.totSaved < loanPayment + toSave) { loanPayment = R.remaining - R.totSaved ; toSave = 0 ; } R.remaining = R.remaining - loanPayment ; int interestOnSaving = calcMonthlyInterest(R.totSaved) ; R.totSaved += interestOnSaving + toSave ; //System.out.println(">>> (" + t + ") " + R.totSaved) ; R.interestPayments.add(interest) ; R.loanPayBacks.add(loanPayment) ; R.remainingLoan.add(R.remaining) ; R.saved.add(toSave) ; R.progressTotalSaved.add(R.totSaved) ; t++ ; } calcTaxReduction(R) ; calcCummulativeStats(R) ; return R ; } private void calcTaxReduction(Result R) { List interestPayments = new LinkedList(R.interestPayments) ; int N = interestPayments.size() ; int K = 0 ; if (N % 12 != 0) K = 12 - (N % 12) ; for (int i=0; i>> " + interestPayments.size()) ; //System.out.println(">>> " + R.remainingLoan.size()) ; for (int interest : interestPayments) { int tax_deductible_interest = interest ; if (interest > 0) { int remaining = R.remainingLoan.get(t-1) ; if (remaining > pure_hypotheek_loan_base) { int remaining_non_tax_deductibe_loan = remaining - pure_hypotheek_loan_base ; int tax_non_deductible_interest = calcMonthlyInterest(remaining_non_tax_deductibe_loan) ; tax_deductible_interest = Math.max(0, tax_deductible_interest - tax_non_deductible_interest) ; } } cummulativeAnnualInterestPayment += tax_deductible_interest ; // End of a year, calculate tax reduction: if (t % 12 == 0) { // tax reduction over a year: int current_age = age + t/12 ; int taxReduction = incomeTaxSystem.calcHypBelastingAftrek(assumed_income, current_age, cummulativeAnnualInterestPayment) ; int numOfMonth = 12 ; if (t>N) numOfMonth = N % 12 ; int mothlytaxReduction = div(taxReduction,numOfMonth) ; for (int m=0; m calcYearlyCummulativeProgressOfLoanPaid(Result R) { return groupAccumulate(R.loanPayBacks,12) ; } public LinkedList calcYearlyCummulativeProgressOfSaving(Result R) { LinkedList progress = new LinkedList() ; int N = R.progressTotalSaved.size() ; int Y = N / 12 ; int m = 0 ; while (m 0) progress.add(R.progressTotalSaved.getLast()) ; return progress ; } public List calcYearlyCummulativeProgressOfTaxReduction(Result R) { return groupAccumulate(R.taxReductions,12) ; } private void calcCummulativeStats(Result R) { R.totLoanPaidBack = loan - R.remaining ; R.totPaidBruto = 0 ; for (int i : R.interestPayments) R.totPaidBruto += i ; for (int a : R.loanPayBacks) R.totPaidBruto += a ; for (int s : R.saved) R.totPaidBruto += s ; R.totInterest = 0 ; for (int i : R.interestPayments) R.totInterest += i ; R.totTaxReduction = 0 ; for (int tr : R.taxReductions) R.totTaxReduction += tr ; R.totPaidNetto = R.totPaidBruto - R.totTaxReduction ; int actualDuration = R.interestPayments.size() ; //System.out.println(">>> " + actualDuration) ; float growth = 1f + toFloat(assumedHousePriceIncrRate) / 10000f ; float projectedHouseValue_ = cummulativeGrowth(getHousePurchaseValue(),growth,toFloat(actualDuration)/12f) ; R.projectedHouseValue = toInt(projectedHouseValue_) ; R.profit = R.projectedHouseValue - R.totPaidNetto - (R.remaining - R.totSaved) ; } /** * Convert months to years, rounded to 1 decimal. */ private float month2year(int months) { float j = Math.round(10.0 * ((float) months) / 12f ) ; return j/10f ; } public String printGenStatistics(Result R) { StringBuffer o = new StringBuffer() ; int W = 31 ; int WD = 12 ; o.append("GEGEVENS:\n") ; o.append(leftAlign("** Lening =",W) + showEuro(loan,WD) + " eur.\n") ; o.append(leftAlign("** Max. leenperiode =",W) + rightAlign(""+duration,WD-3) + " maanden (" + month2year(duration) + " jaar)\n") ; o.append(leftAlign("** Rente =",W) + showEuro(interestRate,WD) + " %\n") ; if (lifeInsurancePremium>0) o.append(leftAlign("** Premie levensverz. =",W) + showEuro(lifeInsurancePremium,WD) + " eur. (per maand)\n") ; o.append(leftAlign("** Maandl. bruto betaling =",W) + showEuro(monthlyPayment,WD) + " eur.\n") ; o.append(leftAlign("** Bijkomende koopkosten =",W) + showEuro(getOverheadCost(),WD) + " eur. (" + (toFloat(purchaseCostPercentage)/100f) + " %)\n" ) ; o.append(leftAlign("** Koopwaarde van uw huis =",W) + showEuro(getHousePurchaseValue(),WD) + " eur.\n") ; o.append(leftAlign("** Bruto jaarsalaris =",W) + showEuro(income,WD) + " eur.\n") ; o.append(leftAlign(" Eigenwoningforfait =",W) + showEuro(getEigenWoningforfait(),WD) + " eur.\n") ; o.append("\nBEREKENING OVERZICHT:\n") ; if (R == null) { int interest = calcMonthlyInterest(loan) ; int minMpay = interest/100 + lifeInsurancePremium ; o.append(" Uw voorstel is niet realiseerbaar.\n") ; o.append(" Leen minder, of zet maandelijksbetaling op minstens " + minMpay + " eur.\n") ; return o.toString() ; } int actualDuration = R.loanPayBacks.size() ; if (actualDuration < duration) o.append(leftAlign("** Werkelijke leenperiode =",W) + rightAlign("" + actualDuration,WD-3) + " maanden (" + month2year(actualDuration) + " jaar)\n") ; o.append(leftAlign("** Overgeblevende lening =",W) + showEuro(R.remaining - R.totSaved,WD) + " eur.\n") ; o.append(leftAlign("** Totaal afgelost =",W) + showEuro(loan - R.remaining,WD) + " eur.\n") ; if (percentageToSave>0) { //System.out.println(">>>" + R.totSaved) ; o.append(leftAlign("** Totaal gespaard =",W) + showEuro(R.totSaved,WD) + " eur.\n") ; } o.append(leftAlign("** Totaal kwijt aan rente =",W) + showEuro(R.totInterest,WD) + " eur.\n") ; o.append(leftAlign("** Totaal bruto betaald =",W) + showEuro(R.totPaidBruto,WD) + " eur.\n") ; o.append(leftAlign("** Totaal netto betaald =",W) + showEuro(R.totPaidNetto,WD) + " eur.\n") ; o.append(leftAlign("** Totaal belst. aftrek =",W) + showEuro(R.totTaxReduction,WD) + " eur.\n") ; double priceGrowthPercetage = round(toDouble(assumedHousePriceIncrRate)/100d ,2) ; o.append(leftAlign("** Eindwaarde huis =",W) + showEuro(R.projectedHouseValue,WD) + " eur." + " (op basis van " + priceGrowthPercetage + " % prijsgroei)\n" ) ; o.append(leftAlign("** Uw resultaat =",W) + showEuro(R.profit,WD) + " eur.\n" ) ; return o.toString() ; } public String printYearReport(Result R, int year) { int start = 12 * (year - 1) ; int N = R.interestPayments.size() ; assert start < N : "PRE" ; int end = Math.min(start+12, N) ; int totInterest = 0 ; int totLoanPaidBack = 0 ; int totBrutoPaid = 0 ; int totTaxReduction = 0 ; int totNettoPaid = 0 ; int remaining = loan ; int paidForSaving = 0 ; for (int k=start; k0) { o.append(leftAlign("** Betaald voor sparen =",W) + showEuro(paidForSaving,WD) + " eur.\n") ; } o.append(leftAlign("** Betaald aan rente =",W) + showEuro(totInterest,WD) + " eur.\n") ; if (lifeInsurancePremium>0) o.append(leftAlign("** Levensverz. premie =",W) + showEuro(lifeInsurancePremium*(end-start),WD) + " eur.\n") ; o.append(leftAlign("** Totaal bruto betaald =",W) + showEuro(totBrutoPaid,WD) + " eur.\n") ; o.append(leftAlign("** Totaal netto betaald =",W) + showEuro(totNettoPaid,WD) + " eur.\n") ; o.append(leftAlign("** Belasting aftrek =",W) + showEuro(totTaxReduction,WD) + " eur.\n") ; o.append(leftAlign("** Overgeblevende lening =",W) + showEuro(remaining,WD) + " eur.\n") ; if (percentageToSave>0) { o.append(leftAlign("** Tot nu toe gespaard =",W) + showEuro(totSaved,WD) + " eur.\n") ; } o.append("\nMAANDELIJKSBETALINGEN :\n") ; int W2 = 30 ; for (int k=start; k0) o.append(leftAlign(" Levensverz. premie =",W2) + showEuro(lifeInsurancePremium,WD) + " eur.\n") ; if (percentageToSave>0) { o.append(leftAlign(" Sparen =",W2) + showEuro(R.saved.get(k),WD) + " eur.\n") ; } int brutoSum = R.interestPayments.get(k) + R.loanPayBacks.get(k) + R.saved.get(k); int nettoSum = brutoSum - R.taxReductions.get(k) ; o.append(leftAlign(" Bruto betaald =",W2) + showEuro(brutoSum,WD) + " eur.\n") ; o.append(leftAlign(" Netto betaald =",W2) + showEuro(nettoSum,WD) + " eur.\n") ; } return o.toString() ; } static public void main(String[] args) { Hypotheek H = new Hypotheek() ; H.setLoan(250000 * 100) ; H.setDuration(MAX_LOAN_DURATION) ; H.setIterest((int) (4.5f * 100f)) ; H.setIncome(40000 * 100) ; H.setMonthlyPayment(1150 * 100) ; Result R = H.calculate() ; System.out.println(H.printGenStatistics(R)) ; System.out.println(H.printYearReport(R,1)) ; System.out.println(H.printYearReport(R,30)) ; } }