/**
* 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)) ;
}
}