diff --git a/pom.xml b/pom.xml
index 4236b41d..4498d407 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,6 +24,11 @@
mason
19
+
+ com.typesafe
+ config
+ 1.3.1
+
org.apache.commons
commons-math3
diff --git a/src/main/java/collectors/CreditSupply.java b/src/main/java/collectors/CreditSupply.java
index fd007e70..a2ccd978 100644
--- a/src/main/java/collectors/CreditSupply.java
+++ b/src/main/java/collectors/CreditSupply.java
@@ -8,31 +8,30 @@
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
public class CreditSupply extends CollectorBase {
- private static final long serialVersionUID = 1630707025974306844L;
-
- private Config config = Model.config; // Passes the Model's configuration parameters object to a private field
-
- public CreditSupply() {
- mortgageCounter = 0;
- ftbCounter = 0;
- btlCounter = 0;
- // TODO: This limit in the number of events taken into account to build statistics is not explained in the paper (affects oo_lti, oo_ltv, btl_ltv, btl_icr, downpayments)
- setArchiveLength(10000);
- }
+ private static final long serialVersionUID = 1630707025974306844L;
+
+
+ public CreditSupply() {
+ mortgageCounter = 0;
+ ftbCounter = 0;
+ btlCounter = 0;
+ // TODO: This limit in the number of events taken into account to build statistics is not explained in the paper (affects oo_lti, oo_ltv, btl_ltv, btl_icr, downpayments)
+ setArchiveLength(10000);
+ }
- /***
- * collect information for this timestep
- */
- public void step() {
+ /***
+ * collect information for this timestep
+ */
+ public void step() {
double oldTotalCredit = totalOOCredit + totalBTLCredit;
totalOOCredit = 0.0;
totalBTLCredit = 0.0;
for(MortgageAgreement m : Model.bank.mortgages) {
- if(m.isBuyToLet) {
- totalBTLCredit += m.principal;
- } else {
- totalOOCredit += m.principal;
- }
+ if(m.isBuyToLet) {
+ totalBTLCredit += m.principal;
+ } else {
+ totalOOCredit += m.principal;
+ }
}
netCreditGrowth = (totalOOCredit + totalBTLCredit - oldTotalCredit)/oldTotalCredit;
nApprovedMortgages = mortgageCounter;
@@ -41,55 +40,55 @@ public void step() {
mortgageCounter = 0;
ftbCounter = 0;
btlCounter = 0;
- }
+ }
- /***
- * record information for a newly issued mortgage
- * @param h
- * @param approval
- */
- public void recordLoan(Household h, MortgageAgreement approval, House house) {
- double housePrice;
- if(config.isMortgageDiagnosticsActive()) {
- housePrice = approval.principal + approval.downPayment;
- affordability = config.derivedParams.getAffordabilityDecay()*affordability + (1.0-config.derivedParams.getAffordabilityDecay())*approval.monthlyPayment/(h.monthlyEmploymentIncome);
- if(approval.principal > 1.0) {
- if(approval.isBuyToLet) {
- btl_ltv.addValue(100.0*approval.principal/housePrice);
-// double icr = Model.rentalMarket.getAverageSalePrice(house.getQuality())*12.0/(approval.principal*Model.bank.getBtLStressedMortgageInterestRate());
- double icr = Model.rentalMarket.averageSoldGrossYield*approval.purchasePrice/(approval.principal*config.getCentralBankBTLStressedInterest());
- btl_icr.addValue(icr);
- } else {
- oo_ltv.addValue(100.0*approval.principal/housePrice);
- oo_lti.addValue(approval.principal/h.annualEmploymentIncome());
- }
- downpayments.addValue(approval.downPayment);
- }
-// approved_mortgages[0][approved_mortgages_index] = approval.principal/(h.annualEmploymentIncome());
-// approved_mortgages[1][approved_mortgages_index] = approval.downPayment/(h.annualEmploymentIncome());
-// approved_mortgages_index += 1;
-// if(approved_mortgages_index == ARCHIVE_LEN) approved_mortgages_index = 0;
- mortgageCounter += 1;
- if(approval.isFirstTimeBuyer) ftbCounter += 1;
- if(approval.isBuyToLet) btlCounter += 1;
- }
- }
-
+ /***
+ * record information for a newly issued mortgage
+ * @param h
+ * @param approval
+ */
+ public void recordLoan(Household h, MortgageAgreement approval, House house) {
+ double housePrice;
+ if(config.isMortgageDiagnosticsActive()) {
+ housePrice = approval.principal + approval.downPayment;
+ affordability = config.derivedParams.getAffordabilityDecay()*affordability + (1.0-config.derivedParams.getAffordabilityDecay())*approval.monthlyPayment/(h.monthlyEmploymentIncome);
+ if(approval.principal > 1.0) {
+ if(approval.isBuyToLet) {
+ btl_ltv.addValue(100.0*approval.principal/housePrice);
+// double icr = Model.rentalMarket.getAverageSalePrice(house.getQuality())*12.0/(approval.principal*Model.bank.getBtLStressedMortgageInterestRate());
+ double icr = Model.rentalMarket.averageSoldGrossYield*approval.purchasePrice/(approval.principal*config.getCentralBankBTLStressedInterest());
+ btl_icr.addValue(icr);
+ } else {
+ oo_ltv.addValue(100.0*approval.principal/housePrice);
+ oo_lti.addValue(approval.principal/h.annualEmploymentIncome());
+ }
+ downpayments.addValue(approval.downPayment);
+ }
+// approved_mortgages[0][approved_mortgages_index] = approval.principal/(h.annualEmploymentIncome());
+// approved_mortgages[1][approved_mortgages_index] = approval.downPayment/(h.annualEmploymentIncome());
+// approved_mortgages_index += 1;
+// if(approved_mortgages_index == ARCHIVE_LEN) approved_mortgages_index = 0;
+ mortgageCounter += 1;
+ if(approval.isFirstTimeBuyer) ftbCounter += 1;
+ if(approval.isBuyToLet) btlCounter += 1;
+ }
+ }
+
- // ---- Mason stuff
- // ----------------
- public String desLTI() {return("Loan to Income constraint on mortgages");}
- public String desTHETA_FTB() {return("Loan to Value haircut for first time buyers");}
- public String desTHETA_HOME() {return("Loan to Value haircut for homeowners");}
- public String desTHETA_BTL() {return("Loan to Value haircut for buy-to-let investors");}
- public String desN_PAYMENTS() {return("Number of monthly repayments in a mortgage");}
- public double getBaseRate() {
- return Model.bank.getBaseRate();
- }
- public void setBaseRate(double rate) {
- Model.bank.setBaseRate(rate);
- }
-
+ // ---- Mason stuff
+ // ----------------
+ public String desLTI() {return("Loan to Income constraint on mortgages");}
+ public String desTHETA_FTB() {return("Loan to Value haircut for first time buyers");}
+ public String desTHETA_HOME() {return("Loan to Value haircut for homeowners");}
+ public String desTHETA_BTL() {return("Loan to Value haircut for buy-to-let investors");}
+ public String desN_PAYMENTS() {return("Number of monthly repayments in a mortgage");}
+ public double getBaseRate() {
+ return Model.bank.getBaseRate();
+ }
+ public void setBaseRate(double rate) {
+ Model.bank.setBaseRate(rate);
+ }
+
public double [] getOOLTVDistribution() {return(oo_ltv.getValues());}
public double [] getOOLTIDistribution() {return(oo_lti.getValues());}
public double [] getBTLLTVDistribution() {return(btl_ltv.getValues());}
@@ -115,53 +114,53 @@ public void setSaveBTLICRDistribution(boolean doSave) throws FileNotFoundExcepti
public int getNRegisteredMortgages() {
- return(Model.bank.mortgages.size());
+ return(Model.bank.mortgages.size());
}
- public int getArchiveLength() {
- return archiveLength;
- }
-
- public void writeDistributionToFile(double [] vals, String filename) throws FileNotFoundException, UnsupportedEncodingException {
+ public int getArchiveLength() {
+ return archiveLength;
+ }
+
+ public void writeDistributionToFile(double [] vals, String filename) throws FileNotFoundException, UnsupportedEncodingException {
PrintWriter dist = new PrintWriter(filename, "UTF-8");
if(vals.length > 0) {
- dist.print(vals[0]);
- for(int i=1; i();
- init();
- }
-
- public void init() {
- mortgages.clear();
- baseRate = config.BANK_INITIAL_BASE_RATE;
- // TODO: Is this (dDemand_dInterest) a parameter? Shouldn't it depend somehow on other variables of the model?
- dDemand_dInterest = 10*1e10;
+// public double INTEREST_MARGIN = 0.03; // Interest rate rise in affordability stress test (http://www.bankofengland.co.uk/financialstability/Pages/fpc/intereststress.aspx)
+ private BankConfig config;
+
+ /********************************
+ * Constructor. This just sets up a few
+ * pre-computed values.
+ ********************************/
+ public Bank(BankConfig config) {
+ this.config = config;
+ mortgages = new HashSet<>();
+ init();
+ }
+
+ public void init() {
+ mortgages.clear();
+ baseRate = config.getInitialBaseRate();
+ // TODO: Is this (dDemand_dInterest) a parameter? Shouldn't it depend somehow on other variables of the model?
+ dDemand_dInterest = 10*1e10;
// TODO: Is this (0.02) a parameter? Does it affect results in any significant way or is it just a dummy initialisation?
setMortgageInterestRate(0.02);
- resetMonthlyCounters();
+ resetMonthlyCounters();
+ }
+
+ /***
+ * This is where the bank gets to do its monthly calculations
+ */
+ public void step() {
+ supplyTarget = config.getCreditSupplyTarget() * Model.households.size();
+ setMortgageInterestRate(recalcInterestRate());
+ resetMonthlyCounters();
+ }
+
+ /***
+ * Resets all the various monthly diagnostic measures ready for the next month
+ */
+ public void resetMonthlyCounters() {
+ lastMonthsSupplyVal = supplyVal;
+ demand = 0.0;
+ supplyVal = 0.0;
+ nLoans = 0;
+ nOverLTICapLoans = 0;
+ nOverLTVCapLoans = 0;
}
-
- /***
- * This is where the bank gets to do its monthly calculations
- */
- public void step() {
- supplyTarget = config.BANK_CREDIT_SUPPLY_TARGET*Model.households.size();
- setMortgageInterestRate(recalcInterestRate());
- resetMonthlyCounters();
- }
-
- /***
- * Resets all the various monthly diagnostic measures ready for the next month
- */
- public void resetMonthlyCounters() {
- lastMonthsSupplyVal = supplyVal;
- demand = 0.0;
- supplyVal = 0.0;
- nLoans = 0;
- nOverLTICapLoans = 0;
- nOverLTVCapLoans = 0;
- }
-
- /***
- * Calculates the next months mortgage interest based on this months
- * rate and the resulting demand.
- *
- * Assumes a linear relationship between interest rate and demand,
- * and aims to halve the difference between current demand
- * and target supply
- */
- public double recalcInterestRate() {
- double rate = getMortgageInterestRate() + 0.5*(supplyVal - supplyTarget)/dDemand_dInterest;
-// System.out.println(supplyVal/Model.households.size());
- if(rate < baseRate) rate = baseRate;
- return(rate);
- }
-
- /******************************
- * Get the interest rate on mortgages.
- * @return The interest rate on mortgages.
- *****************************/
- public double getMortgageInterestRate() {
- return(baseRate + interestSpread);
- }
-
+
+ /***
+ * Calculates the next months mortgage interest based on this months
+ * rate and the resulting demand.
+ *
+ * Assumes a linear relationship between interest rate and demand,
+ * and aims to halve the difference between current demand
+ * and target supply
+ */
+ public double recalcInterestRate() {
+ double rate = getMortgageInterestRate() + 0.5*(supplyVal - supplyTarget)/dDemand_dInterest;
+// System.out.println(supplyVal/Model.households.size());
+ if(rate < baseRate) rate = baseRate;
+ return(rate);
+ }
+
+ /******************************
+ * Get the interest rate on mortgages.
+ * @return The interest rate on mortgages.
+ *****************************/
+ public double getMortgageInterestRate() {
+ return(baseRate + interestSpread);
+ }
+
- /******************************
- * Get the interest rate on mortgages.
- * @return The interest rate on mortgages.
- *****************************/
- public void setMortgageInterestRate(double rate) {
- interestSpread = rate - baseRate;
- recalculateK();
- }
-
- public double getBaseRate() {
- return baseRate;
- }
+ /******************************
+ * Get the interest rate on mortgages.
+ * @return The interest rate on mortgages.
+ *****************************/
+ public void setMortgageInterestRate(double rate) {
+ interestSpread = rate - baseRate;
+ recalculateK();
+ }
+
+ public double getBaseRate() {
+ return baseRate;
+ }
- public void setBaseRate(double baseRate) {
- this.baseRate = baseRate;
- recalculateK();
- }
+ public void setBaseRate(double baseRate) {
+ this.baseRate = baseRate;
+ recalculateK();
+ }
- protected void recalculateK() {
- double r = getMortgageInterestRate()/config.constants.MONTHS_IN_YEAR;
- k = r/(1.0 - Math.pow(1.0+r, -config.derivedParams.N_PAYMENTS));
- }
+ protected void recalculateK() {
+ double r = getMortgageInterestRate()/config.constants.MONTHS_IN_YEAR;
+ k = r/(1.0 - Math.pow(1.0+r, -config.derivedParams.N_PAYMENTS));
+ }
- /*******************************
- * Get the monthly payment on a mortgage as a fraction of the mortgage principle.
- * @return The monthly payment fraction.
- *******************************/
- public double monthlyPaymentFactor(boolean isHome) {
- if(isHome) {
- return(k); // Pay off in N_PAYMENTS
- } else {
- return(getMortgageInterestRate()/config.constants.MONTHS_IN_YEAR); // interest only
- }
- }
+ /*******************************
+ * Get the monthly payment on a mortgage as a fraction of the mortgage principle.
+ * @return The monthly payment fraction.
+ *******************************/
+ public double monthlyPaymentFactor(boolean isHome) {
+ if(isHome) {
+ return(k); // Pay off in N_PAYMENTS
+ } else {
+ return(getMortgageInterestRate()/config.constants.MONTHS_IN_YEAR); // interest only
+ }
+ }
- /*
- public double stressedMonthlyPaymentFactor(boolean isHome) {
- if(isHome) {
- return(k); // Fixed rate for OO
- } else {
- return((getMortgageInterestRate()+INTEREST_MARGIN)/12.0); // interest only
- }
- }
+ /*
+ public double stressedMonthlyPaymentFactor(boolean isHome) {
+ if(isHome) {
+ return(k); // Fixed rate for OO
+ } else {
+ return((getMortgageInterestRate()+INTEREST_MARGIN)/12.0); // interest only
+ }
+ }
*/
- /*****************************
- * Use this to arrange a Mortgage and get a MortgageApproval object.
- *
- * @param h The household that is requesting the mortgage.
- * @param housePrice The price of the house that 'h' wants to buy
- * @param isHome true if 'h' plans to live in the house.
- * @return The MortgageApproval object, or NULL if the mortgage is declined
- ****************************/
- public MortgageAgreement requestLoan(Household h, double housePrice, double desiredDownPayment, boolean isHome, House house) {
- MortgageAgreement approval = requestApproval(h, housePrice, desiredDownPayment, isHome);
- if(approval == null) return(null);
- // --- if all's well, go ahead and arrange mortgage
- supplyVal += approval.principal;
- if(approval.principal > 0.0) {
- mortgages.add(approval);
- Model.collectors.creditSupply.recordLoan(h, approval, house);
- ++nLoans;
- if(isHome) {
- if(approval.principal/h.annualEmploymentIncome() > Model.centralBank.loanToIncomeRegulation(h.isFirstTimeBuyer())) {
- ++nOverLTICapLoans;
- }
- if(approval.principal/(approval.principal + approval.downPayment) > Model.centralBank.loanToValueRegulation(h.isFirstTimeBuyer(),isHome)) {
- ++nOverLTVCapLoans;
- }
- }
- }
- return(approval);
- }
-
-
- public void endMortgageContract(MortgageAgreement mortgage) {
- mortgages.remove(mortgage);
- }
+ /*****************************
+ * Use this to arrange a Mortgage and get a MortgageApproval object.
+ *
+ * @param h The household that is requesting the mortgage.
+ * @param housePrice The price of the house that 'h' wants to buy
+ * @param isHome true if 'h' plans to live in the house.
+ * @return The MortgageApproval object, or NULL if the mortgage is declined
+ ****************************/
+ public MortgageAgreement requestLoan(Household h, double housePrice, double desiredDownPayment, boolean isHome, House house) {
+ MortgageAgreement approval = requestApproval(h, housePrice, desiredDownPayment, isHome);
+ if(approval == null) return(null);
+ // --- if all's well, go ahead and arrange mortgage
+ supplyVal += approval.principal;
+ if(approval.principal > 0.0) {
+ mortgages.add(approval);
+ Model.collectors.creditSupply.recordLoan(h, approval, house);
+ ++nLoans;
+ if(isHome) {
+ if(approval.principal/h.annualEmploymentIncome() > Model.centralBank.loanToIncomeRegulation(h.isFirstTimeBuyer())) {
+ ++nOverLTICapLoans;
+ }
+ if(approval.principal/(approval.principal + approval.downPayment) > Model.centralBank.loanToValueRegulation(h.isFirstTimeBuyer(),isHome)) {
+ ++nOverLTVCapLoans;
+ }
+ }
+ }
+ return(approval);
+ }
+
+
+ public void endMortgageContract(MortgageAgreement mortgage) {
+ mortgages.remove(mortgage);
+ }
- /********
- * Use this to request a mortgage approval but not actually sign a mortgage contract.
- * This is useful if you want to inspect the details of the mortgage contract before
- * deciding whether to actually go ahead and sign.
- *
- * @param h The household that is requesting the approval.
- * @param housePrice The price of the house that 'h' wants to buy
- * @param isHome does 'h' plan to live in the house?
- * @return A MortgageApproval object, or NULL if the mortgage is declined
- */
- public MortgageAgreement requestApproval(Household h, double housePrice, double desiredDownPayment, boolean isHome) {
- MortgageAgreement approval = new MortgageAgreement(h, !isHome);
- double r = getMortgageInterestRate()/config.constants.MONTHS_IN_YEAR; // monthly interest rate
- double lti_principal, affordable_principal, icr_principal;
- double liquidWealth = h.getBankBalance();
-
- if(isHome) liquidWealth += h.getHomeEquity();
+ /********
+ * Use this to request a mortgage approval but not actually sign a mortgage contract.
+ * This is useful if you want to inspect the details of the mortgage contract before
+ * deciding whether to actually go ahead and sign.
+ *
+ * @param h The household that is requesting the approval.
+ * @param housePrice The price of the house that 'h' wants to buy
+ * @param isHome does 'h' plan to live in the house?
+ * @return A MortgageApproval object, or NULL if the mortgage is declined
+ */
+ public MortgageAgreement requestApproval(Household h, double housePrice, double desiredDownPayment, boolean isHome) {
+ MortgageAgreement approval = new MortgageAgreement(h, !isHome);
+ double r = getMortgageInterestRate()/config.constants.MONTHS_IN_YEAR; // monthly interest rate
+ double lti_principal, affordable_principal, icr_principal;
+ double liquidWealth = h.getBankBalance();
+
+ if(isHome) liquidWealth += h.getHomeEquity();
- // --- LTV constraint
- approval.principal = housePrice*loanToValue(h.isFirstTimeBuyer(), isHome);
+ // --- LTV constraint
+ approval.principal = housePrice*loanToValue(h.isFirstTimeBuyer(), isHome);
- if(isHome) {
- // --- affordability constraint TODO: affordability for BtL?
- affordable_principal = Math.max(0.0,config.CENTRAL_BANK_AFFORDABILITY_COEFF*h.getMonthlyPostTaxIncome())/monthlyPaymentFactor(isHome);
- approval.principal = Math.min(approval.principal, affordable_principal);
+ if(isHome) {
+ // --- affordability constraint TODO: affordability for BtL?
+ affordable_principal = Math.max(0.0,config.CENTRAL_BANK_AFFORDABILITY_COEFF*h.getMonthlyPostTaxIncome())/monthlyPaymentFactor(isHome);
+ approval.principal = Math.min(approval.principal, affordable_principal);
- // --- lti constraint
- lti_principal = h.annualEmploymentIncome() * loanToIncome(h.isFirstTimeBuyer());
- approval.principal = Math.min(approval.principal, lti_principal);
- } else {
- // --- BtL ICR constraint
- icr_principal = Model.rentalMarket.averageSoldGrossYield*housePrice/(interestCoverageRatio()*config.CENTRAL_BANK_BTL_STRESSED_INTEREST);
- approval.principal = Math.min(approval.principal, icr_principal);
- // System.out.println(icr_principal/housePrice);
- }
-
- approval.downPayment = housePrice - approval.principal;
-
- if(liquidWealth < approval.downPayment) {
- System.out.println("Failed down-payment constraint: bank balance = "+liquidWealth+" Downpayment = "+approval.downPayment);
- System.out.println("isHome = "+isHome+" isFirstTimeBuyer = "+h.isFirstTimeBuyer());
- approval.downPayment = liquidWealth;
-// return(null);
- }
- // --- allow larger downpayments
- if(desiredDownPayment < 0.0) desiredDownPayment = 0.0;
- if(desiredDownPayment > liquidWealth) desiredDownPayment = liquidWealth;
- if(desiredDownPayment > housePrice) desiredDownPayment = housePrice;
- if(desiredDownPayment > approval.downPayment) {
- approval.downPayment = desiredDownPayment;
- approval.principal = housePrice - desiredDownPayment;
- }
-
- approval.monthlyPayment = approval.principal*monthlyPaymentFactor(isHome);
- approval.nPayments = config.derivedParams.N_PAYMENTS;
- approval.monthlyInterestRate = r;
-// approval.isFirstTimeBuyer = h.isFirstTimeBuyer();
- approval.purchasePrice = approval.principal + approval.downPayment;
- return(approval);
- }
+ // --- lti constraint
+ lti_principal = h.annualEmploymentIncome() * loanToIncome(h.isFirstTimeBuyer());
+ approval.principal = Math.min(approval.principal, lti_principal);
+ } else {
+ // --- BtL ICR constraint
+ icr_principal = Model.rentalMarket.averageSoldGrossYield*housePrice/(interestCoverageRatio()*config.CENTRAL_BANK_BTL_STRESSED_INTEREST);
+ approval.principal = Math.min(approval.principal, icr_principal);
+ // System.out.println(icr_principal/housePrice);
+ }
+
+ approval.downPayment = housePrice - approval.principal;
+
+ if(liquidWealth < approval.downPayment) {
+ System.out.println("Failed down-payment constraint: bank balance = "+liquidWealth+" Downpayment = "+approval.downPayment);
+ System.out.println("isHome = "+isHome+" isFirstTimeBuyer = "+h.isFirstTimeBuyer());
+ approval.downPayment = liquidWealth;
+// return(null);
+ }
+ // --- allow larger downpayments
+ if(desiredDownPayment < 0.0) desiredDownPayment = 0.0;
+ if(desiredDownPayment > liquidWealth) desiredDownPayment = liquidWealth;
+ if(desiredDownPayment > housePrice) desiredDownPayment = housePrice;
+ if(desiredDownPayment > approval.downPayment) {
+ approval.downPayment = desiredDownPayment;
+ approval.principal = housePrice - desiredDownPayment;
+ }
+
+ approval.monthlyPayment = approval.principal*monthlyPaymentFactor(isHome);
+ approval.nPayments = config.derivedParams.N_PAYMENTS;
+ approval.monthlyInterestRate = r;
+// approval.isFirstTimeBuyer = h.isFirstTimeBuyer();
+ approval.purchasePrice = approval.principal + approval.downPayment;
+ return(approval);
+ }
- /*****************************************
- * Find the maximum mortgage that this mortgage-lender will approve
- * to a household.
- *
- * @param h household who is applying for the mortgage
- * @param isHome true if 'h' plans to live in the house
- * @return The maximum value of house that this mortgage-lender is willing
- * to approve a mortgage for.
- ****************************************/
- public double getMaxMortgage(Household h, boolean isHome) {
- double max;
- double pdi_max; // disposable income constraint
- double lti_max; // loan to income constraint
- double icr_max; // interest rate coverage
- double liquidWealth = h.getBankBalance();
+ /*****************************************
+ * Find the maximum mortgage that this mortgage-lender will approve
+ * to a household.
+ *
+ * @param h household who is applying for the mortgage
+ * @param isHome true if 'h' plans to live in the house
+ * @return The maximum value of house that this mortgage-lender is willing
+ * to approve a mortgage for.
+ ****************************************/
+ public double getMaxMortgage(Household h, boolean isHome) {
+ double max;
+ double pdi_max; // disposable income constraint
+ double lti_max; // loan to income constraint
+ double icr_max; // interest rate coverage
+ double liquidWealth = h.getBankBalance();
- if(isHome) {
- liquidWealth += h.getHomeEquity(); // assume h will sell current home
- }
-
- max = liquidWealth/(1.0 - loanToValue(h.isFirstTimeBuyer(), isHome)); // LTV constraint
+ if(isHome) {
+ liquidWealth += h.getHomeEquity(); // assume h will sell current home
+ }
+
+ max = liquidWealth/(1.0 - loanToValue(h.isFirstTimeBuyer(), isHome)); // LTV constraint
- if(isHome) { // no LTI for BtL investors
-// lti_max = h.getMonthlyPreTaxIncome()*12.0* loanToIncome(h.isFirstTimeBuyer())/loanToValue(h.isFirstTimeBuyer(),isHome);
- pdi_max = liquidWealth + Math.max(0.0,config.CENTRAL_BANK_AFFORDABILITY_COEFF*h.getMonthlyPostTaxIncome())/monthlyPaymentFactor(isHome);
- max = Math.min(max, pdi_max);
- lti_max = h.annualEmploymentIncome()* loanToIncome(h.isFirstTimeBuyer()) + liquidWealth;
- max = Math.min(max, lti_max);
- } else {
- icr_max = Model.rentalMarket.averageSoldGrossYield/(interestCoverageRatio()*config.CENTRAL_BANK_BTL_STRESSED_INTEREST);
- if(icr_max < 1.0) {
- icr_max = liquidWealth/(1.0 - icr_max);
- max = Math.min(max, icr_max);
- }
- }
-
- max = Math.floor(max*100.0)/100.0; // round down to nearest penny
- return(max);
- }
+ if(isHome) { // no LTI for BtL investors
+// lti_max = h.getMonthlyPreTaxIncome()*12.0* loanToIncome(h.isFirstTimeBuyer())/loanToValue(h.isFirstTimeBuyer(),isHome);
+ pdi_max = liquidWealth + Math.max(0.0,config.CENTRAL_BANK_AFFORDABILITY_COEFF*h.getMonthlyPostTaxIncome())/monthlyPaymentFactor(isHome);
+ max = Math.min(max, pdi_max);
+ lti_max = h.annualEmploymentIncome()* loanToIncome(h.isFirstTimeBuyer()) + liquidWealth;
+ max = Math.min(max, lti_max);
+ } else {
+ icr_max = Model.rentalMarket.averageSoldGrossYield/(interestCoverageRatio()*config.CENTRAL_BANK_BTL_STRESSED_INTEREST);
+ if(icr_max < 1.0) {
+ icr_max = liquidWealth/(1.0 - icr_max);
+ max = Math.min(max, icr_max);
+ }
+ }
+
+ max = Math.floor(max*100.0)/100.0; // round down to nearest penny
+ return(max);
+ }
- /**********************************************
- * Get the Loan-To-Value ratio applicable to a given household.
- *
- * @param firstTimeBuyer true if the household is a first time buyer
- * @param isHome true if the household plans to live in the house
- * @return The loan-to-value ratio applicable to the given household.
- *********************************************/
- public double loanToValue(boolean firstTimeBuyer, boolean isHome) {
- double limit;
- if(isHome) {
- limit = config.CENTRAL_BANK_MAX_OO_LTV;
- } else {
- limit = config.CENTRAL_BANK_MAX_BTL_LTV;
- }
- if((nOverLTVCapLoans+1.0)/(nLoans + 1.0) > Model.centralBank.proportionOverLTVLimit) {
- limit = Math.min(limit, Model.centralBank.loanToValueRegulation(firstTimeBuyer, isHome));
- }
- return(limit);
- }
+ /**********************************************
+ * Get the Loan-To-Value ratio applicable to a given household.
+ *
+ * @param firstTimeBuyer true if the household is a first time buyer
+ * @param isHome true if the household plans to live in the house
+ * @return The loan-to-value ratio applicable to the given household.
+ *********************************************/
+ public double loanToValue(boolean firstTimeBuyer, boolean isHome) {
+ double limit;
+ if(isHome) {
+ limit = config.CENTRAL_BANK_MAX_OO_LTV;
+ } else {
+ limit = config.CENTRAL_BANK_MAX_BTL_LTV;
+ }
+ if((nOverLTVCapLoans+1.0)/(nLoans + 1.0) > Model.centralBank.proportionOverLTVLimit) {
+ limit = Math.min(limit, Model.centralBank.loanToValueRegulation(firstTimeBuyer, isHome));
+ }
+ return(limit);
+ }
- /**********************************************
- * Get the Loan-To-Income ratio applicable to a given household.
- *
- * @param firstTimeBuyer true if the household is a first time buyer
- * @return The loan-to-income ratio applicable to the given household.
- *********************************************/
- public double loanToIncome(boolean firstTimeBuyer) {
- double limit;
- limit = config.CENTRAL_BANK_MAX_OO_LTI;
- if((nOverLTICapLoans+1.0)/(nLoans + 1.0) > Model.centralBank.proportionOverLTILimit) {
- limit = Math.min(limit, Model.centralBank.loanToIncomeRegulation(firstTimeBuyer));
- }
- return(limit);
- }
-
- public double interestCoverageRatio() {
- return(Model.centralBank.interestCoverageRatioRegulation());
- }
+ /**********************************************
+ * Get the Loan-To-Income ratio applicable to a given household.
+ *
+ * @param firstTimeBuyer true if the household is a first time buyer
+ * @return The loan-to-income ratio applicable to the given household.
+ *********************************************/
+ public double loanToIncome(boolean firstTimeBuyer) {
+ double limit;
+ limit = config.CENTRAL_BANK_MAX_OO_LTI;
+ if((nOverLTICapLoans+1.0)/(nLoans + 1.0) > Model.centralBank.proportionOverLTILimit) {
+ limit = Math.min(limit, Model.centralBank.loanToIncomeRegulation(firstTimeBuyer));
+ }
+ return(limit);
+ }
+
+ public double interestCoverageRatio() {
+ return(Model.centralBank.interestCoverageRatioRegulation());
+ }
- // TODO: Remove (no needed anymore)
-// public double getBtLStressedMortgageInterestRate() {
-// return(config.CENTRAL_BANK_BTL_STRESSED_INTEREST);
-// }
-
- public HashSet mortgages; // all unpaid mortgage contracts supplied by the bank
- public double k; // principal to monthly payment factor
- public double interestSpread; // current mortgage interest spread above base rate (monthly rate*12)
- public double baseRate;
- // --- supply strategy stuff
- public double supplyTarget; // target supply of mortgage lending (pounds)
- public double demand; // monthly demand for mortgage loans (pounds)
- public double supplyVal; // monthly supply of mortgage loans (pounds)
- public double lastMonthsSupplyVal;
- public double dDemand_dInterest; // rate of change of demand with interest rate (pounds)
- public int nOverLTICapLoans; // number of (non-BTL) loans above LTI cap this step
- public int nOverLTVCapLoans; // number of (non-BTL) loans above LTV cap this step
- public int nLoans; // total number of non-BTL loans this step
-
+ // TODO: Remove (no needed anymore)
+// public double getBtLStressedMortgageInterestRate() {
+// return(config.CENTRAL_BANK_BTL_STRESSED_INTEREST);
+// }
+
+ public HashSet mortgages; // all unpaid mortgage contracts supplied by the bank
+ public double k; // principal to monthly payment factor
+ public double interestSpread; // current mortgage interest spread above base rate (monthly rate*12)
+ public double baseRate;
+ // --- supply strategy stuff
+ public double supplyTarget; // target supply of mortgage lending (pounds)
+ public double demand; // monthly demand for mortgage loans (pounds)
+ public double supplyVal; // monthly supply of mortgage loans (pounds)
+ public double lastMonthsSupplyVal;
+ public double dDemand_dInterest; // rate of change of demand with interest rate (pounds)
+ public int nOverLTICapLoans; // number of (non-BTL) loans above LTI cap this step
+ public int nOverLTVCapLoans; // number of (non-BTL) loans above LTV cap this step
+ public int nLoans; // total number of non-BTL loans this step
+
}
diff --git a/src/main/java/housing/CentralBank.java b/src/main/java/housing/CentralBank.java
index a56ad5bc..a68a42b4 100644
--- a/src/main/java/housing/CentralBank.java
+++ b/src/main/java/housing/CentralBank.java
@@ -1,82 +1,67 @@
package housing;
+import configuration.CentralBankConfig;
+
import java.io.Serializable;
public class CentralBank implements Serializable {
- private static final long serialVersionUID = -2857716547766065142L;
-
- private Config config = Model.config; // Passes the Model's configuration parameters object to a private field
-
- public CentralBank() {
- // Setup initial values
- firstTimeBuyerLTVLimit = config.CENTRAL_BANK_MAX_FTB_LTV;
- ownerOccupierLTVLimit= config.CENTRAL_BANK_MAX_OO_LTV;
- buyToLetLTVLimit = config.CENTRAL_BANK_MAX_BTL_LTV;
-
- firstTimeBuyerLTILimit = config.CENTRAL_BANK_MAX_FTB_LTI;
- ownerOccupierLTILimit = config.CENTRAL_BANK_MAX_OO_LTI;
-
- proportionOverLTVLimit = config.CENTRAL_BANK_FRACTION_OVER_MAX_LTV;
- proportionOverLTILimit = config.CENTRAL_BANK_FRACTION_OVER_MAX_LTI;
+
+ private static final long serialVersionUID = -2857716547766065142L;
+ private CentralBankConfig config;
- interestCoverRatioLimit = config.CENTRAL_BANK_MAX_ICR;
- }
-
- /***
- * This method implements the policy strategy of the Central Bank.
- * @param coreIndicators The current value of the core indicators
- */
- public void step(collectors.CoreIndicators coreIndicators) {
- /** Use this method to express the policy strategy of the central bank by
- * setting the value of the various limits in response to the current
- * value of the core indicators.
- *
- * Example policy: if house price growth is greater than 0.001 then FTB LTV limit is 0.75
- * otherwise (if house price growth is less than or equal to 0.001)
- * FTB LTV limit is 0.95
- *
- * Example code:
- *
- * if(coreIndicators.getHousePriceGrowth() > 0.001) {
- * firstTimeBuyerLTVLimit = 0.75;
- * } else {
- * firstTimeBuyerLTVLimit = 0.95;
- * }
- */
+ public CentralBank(CentralBankConfig config) {
+ this.config = config;
+ }
+
+ /***
+ * This method implements the policy strategy of the Central Bank.
+ * @param coreIndicators The current value of the core indicators
+ */
+ public void step(collectors.CoreIndicators coreIndicators) {
+ /** Use this method to express the policy strategy of the central bank by
+ * setting the value of the various limits in response to the current
+ * value of the core indicators.
+ *
+ * Example policy: if house price growth is greater than 0.001 then FTB LTV limit is 0.75
+ * otherwise (if house price growth is less than or equal to 0.001)
+ * FTB LTV limit is 0.95
+ *
+ * Example code:
+ *
+ * if(coreIndicators.getHousePriceGrowth() > 0.001) {
+ * firstTimeBuyerLTVLimit = 0.75;
+ * } else {
+ * firstTimeBuyerLTVLimit = 0.95;
+ * }
+ */
- // Include the policy strategy code here:
+ // Include the policy strategy code here:
- }
-
- public double loanToIncomeRegulation(boolean firstTimeBuyer) {
- if(firstTimeBuyer) {
- return(firstTimeBuyerLTILimit);
- }
- return(ownerOccupierLTILimit);
- }
+ }
+
+ public double loanToIncomeRegulation(boolean firstTimeBuyer) {
+ if(firstTimeBuyer) {
+ return config.getMaxFirstTimeBuyersLTI();
+ } else {
+ return config.getMaxOwnerOccupiersLTI();
+ }
+ }
- public double loanToValueRegulation(boolean firstTimeBuyer, boolean isHome) {
- if(isHome) {
- if(firstTimeBuyer) {
- return(firstTimeBuyerLTVLimit);
- }
- return(ownerOccupierLTVLimit);
- }
- return(buyToLetLTVLimit);
- }
-
- public double interestCoverageRatioRegulation() {
- return(interestCoverRatioLimit);
- }
+ public double loanToValueRegulation(boolean firstTimeBuyer, boolean isHome) {
+ if(isHome) {
+ if (firstTimeBuyer) {
+ return config.getMaxFirstTimeBuyerLTV();
+ } else {
+ return config.getMaxOwnerOccupiersLTV();
+ }
+ } else {
+ return config.getMaxBuyToLetLTV();
+ }
+ }
+
+ public double interestCoverageRatioRegulation() {
+ return config.getMaxInterestCoverageRatio();
+ }
- public double ownerOccupierLTILimit; // LTI upper limit for owner-occupiers
- public double ownerOccupierLTVLimit; // LTV upper limit for owner-occupiers
- public double buyToLetLTILimit; // LTI upper limit for Buy-to-let investors
- public double buyToLetLTVLimit; // LTV upper limit for Buy-to-let investors
- public double firstTimeBuyerLTILimit; // LTI upper limit for first-time buyers
- public double firstTimeBuyerLTVLimit; // LTV upper limit for first-time buyers
- public double proportionOverLTILimit; // proportion of mortgages that are allowed to be above the respective LTI limit
- public double proportionOverLTVLimit; // proportion of mortgages that are allowed to be above the respective LTV limit
- public double interestCoverRatioLimit;
}
diff --git a/src/main/java/housing/Config.java b/src/main/java/housing/Config.java
index e48a2806..2fa08b47 100644
--- a/src/main/java/housing/Config.java
+++ b/src/main/java/housing/Config.java
@@ -117,24 +117,11 @@ public class Config {
double BANK_CREDIT_SUPPLY_TARGET; // Bank's target supply of credit per household per month
// Central bank parameters
- double CENTRAL_BANK_MAX_FTB_LTV; // Maximum LTV ratio that the bank would allow for first-time-buyers when not regulated
- double CENTRAL_BANK_MAX_OO_LTV; // Maximum LTV ratio that the bank would allow for owner-occupiers when not regulated
- double CENTRAL_BANK_MAX_BTL_LTV; // Maximum LTV ratio that the bank would allow for BTL investors when not regulated
- double CENTRAL_BANK_FRACTION_OVER_MAX_LTV; // Maximum fraction of mortgages that the bank can give over the LTV ratio limit
- double CENTRAL_BANK_MAX_FTB_LTI; // Maximum LTI ratio that the bank would allow for first-time-buyers when not regulated
- double CENTRAL_BANK_MAX_OO_LTI; // Maximum LTI ratio that the bank would allow for owner-occupiers when not regulated
- double CENTRAL_BANK_FRACTION_OVER_MAX_LTI; // Maximum fraction of mortgages that the bank can give over the LTI ratio limit
- double CENTRAL_BANK_AFFORDABILITY_COEFF; // Maximum fraction of the household's income to be spent on mortgage repayments under stressed conditions
double CENTRAL_BANK_BTL_STRESSED_INTEREST; // Interest rate under stressed condition for BTL investors when calculating interest coverage ratios (ICR)
- double CENTRAL_BANK_MAX_ICR; // Interest coverage ratio (ICR) limit imposed by the central bank
// Construction sector parameters
double CONSTRUCTION_HOUSES_PER_HOUSEHOLD; // Target ratio of houses per household
- // Government parameters
- double GOVERNMENT_PERSONAL_ALLOWANCE_LIMIT; // Maximum personal allowance
- double GOVERNMENT_INCOME_SUPPORT; // Minimum monthly earnings for a married couple from income support
-
// Collectors parameters
double UK_HOUSEHOLDS; // Approximate number of households in UK, used to scale up results for core indicators
boolean MORTGAGE_DIAGNOSTICS_ACTIVE; // Whether to record mortgage statistics
diff --git a/src/main/java/housing/Government.java b/src/main/java/housing/Government.java
index 51b47abc..dad945e7 100644
--- a/src/main/java/housing/Government.java
+++ b/src/main/java/housing/Government.java
@@ -1,5 +1,7 @@
package housing;
+import configuration.GovernmentConfig;
+
/*****************************************
* This class represents the government.
* This is the class where taxation policy should be encoded.
@@ -9,63 +11,70 @@
****************************************/
public class Government {
- private Config config = Model.config; // Passes the Model's configuration parameters object to a private field
+ private GovernmentConfig config;
+
+ /** Create an instance of the `Government` class.
+ *
+ * @param config a `GovernmentConfig` object encapsulating the behavioral parameters.
+ */
+ public Government(GovernmentConfig config) {
+ this.config = config;
+ }
- /******************************************
- * Calculates the income tax due in one year for a given
- * gross annual income. Doesn't account for married couple's allowance.
- *
- * @param grossIncome The gross, annual income in pounds.
- * @return The annual income tax due in pounds.
- ******************************************/
- public double incomeTaxDue(double grossIncome) {
- double tax = bandedPercentage(grossIncome, data.Government.tax.bands, data.Government.tax.rates);
- if(grossIncome > config.GOVERNMENT_PERSONAL_ALLOWANCE_LIMIT) {
- //double personalAllowance = Math.max((grossIncome - config.GOVERNMENT_PERSONAL_ALLOWANCE_LIMIT)/2.0,0.0);
- double personalAllowance = Math.max(
- data.Government.tax.bands[0]-(grossIncome-config.GOVERNMENT_PERSONAL_ALLOWANCE_LIMIT)/2.0,
- 0.0);
- tax += (data.Government.tax.bands[0]-personalAllowance)*data.Government.tax.rates[0]; // TODO: what does this do?
- }
- return(tax);
- }
-
- /***********************************
- * Calculate the class 1 National Insurance Contributions due on a
- * given annual income (under PAYE).
- *
- * @param grossIncome Gross annual income in pounds
- * @return Annual class 1 NICs due.
- **********************************/
- public double class1NICsDue(double grossIncome) {
- return(bandedPercentage(grossIncome, data.Government.nationalInsurance.bands, data.Government.nationalInsurance.rates));
- }
-
- /**********************************
- * Calculate a "banded percentage" on a value.
- * A "banded percentage" is a way of calculating a non-linear
- * function, f(x), widely used by HMRC. The domain of
- * values of f(x) is split into bands: from 0 to x1, from x1 to x2
- * etc. Each band is associated with a percentage p1, p2 etc.
- * The final value of f(x) is the sum of the percentages of each band.
- * So, for example, if x lies somewhere between x1 and x2, f(x) would be
- * p1x1 + p2(x-x1)
- *
- * @param taxableIncome the value to apply the banded percentage to.
- * @param bands an array holding the upper limit of each band
- * @param rates an array holding the percentage applicable to each band
- * @return The banded percentage of 'taxableIncome'
- ***********************************/
- protected double bandedPercentage(double taxableIncome, Double [] bands, Double [] rates) {
- int i = 0;
- double lastRate = 0.0;
- double tax = 0.0;
-
- while(i < bands.length && taxableIncome > bands[i]) {
- tax += (taxableIncome - bands[i])*(rates[i] - lastRate);
- lastRate = rates[i];
- ++i;
- }
- return(tax);
- }
+ /******************************************
+ * Calculates the income tax due in one year for a given
+ * gross annual income. Doesn't account for married couple's allowance.
+ *
+ * @param grossIncome The gross, annual income in pounds.
+ * @return The annual income tax due in pounds.
+ ******************************************/
+ public double incomeTaxDue(double grossIncome) {
+ double tax = bandedPercentage(grossIncome, data.Government.tax.bands, data.Government.tax.rates);
+ if(grossIncome > config.getPersonalAllowanceLimit()) {
+ //double personalAllowance = Math.max((grossIncome - config.GOVERNMENT_PERSONAL_ALLOWANCE_LIMIT)/2.0,0.0);
+ double personalAllowance = Math.max(
+ data.Government.tax.bands[0]-(grossIncome-config.getPersonalAllowanceLimit())/2.0, 0.0);
+ tax += (data.Government.tax.bands[0]-personalAllowance)*data.Government.tax.rates[0]; // TODO: what does this do?
+ }
+ return(tax);
+ }
+
+ /***********************************
+ * Calculate the class 1 National Insurance Contributions due on a
+ * given annual income (under PAYE).
+ *
+ * @param grossIncome Gross annual income in pounds
+ * @return Annual class 1 NICs due.
+ **********************************/
+ public double class1NICsDue(double grossIncome) {
+ return(bandedPercentage(grossIncome, data.Government.nationalInsurance.bands, data.Government.nationalInsurance.rates));
+ }
+
+ /**********************************
+ * Calculate a "banded percentage" on a value.
+ * A "banded percentage" is a way of calculating a non-linear
+ * function, f(x), widely used by HMRC. The domain of
+ * values of f(x) is split into bands: from 0 to x1, from x1 to x2
+ * etc. Each band is associated with a percentage p1, p2 etc.
+ * The final value of f(x) is the sum of the percentages of each band.
+ * So, for example, if x lies somewhere between x1 and x2, f(x) would be
+ * p1x1 + p2(x-x1)
+ *
+ * @param taxableIncome the value to apply the banded percentage to.
+ * @param bands an array holding the upper limit of each band
+ * @param rates an array holding the percentage applicable to each band
+ * @return The banded percentage of 'taxableIncome'
+ ***********************************/
+ private double bandedPercentage(double taxableIncome, Double [] bands, Double [] rates) {
+ int i = 0;
+ double lastRate = 0.0;
+ double tax = 0.0;
+
+ while(i < bands.length && taxableIncome > bands[i]) {
+ tax += (taxableIncome - bands[i])*(rates[i] - lastRate);
+ lastRate = rates[i];
+ ++i;
+ }
+ return(tax);
+ }
}
diff --git a/src/main/java/housing/House.java b/src/main/java/housing/House.java
index 0b2ad27a..5ea064bd 100644
--- a/src/main/java/housing/House.java
+++ b/src/main/java/housing/House.java
@@ -12,8 +12,6 @@
public class House implements Comparable, Serializable {
private static final long serialVersionUID = 4538336934216907799L;
- private Config config = Model.config; // Passes the Model's configuration parameters object to a private field
-
// static public class Config {
// public static int N_QUALITY = 48; // number of quality bands
// }
diff --git a/src/main/java/housing/HouseSaleMarket.java b/src/main/java/housing/HouseSaleMarket.java
index 5bb7e556..2b9e34b7 100644
--- a/src/main/java/housing/HouseSaleMarket.java
+++ b/src/main/java/housing/HouseSaleMarket.java
@@ -15,8 +15,6 @@
public class HouseSaleMarket extends HousingMarket {
private static final long serialVersionUID = -2878118108039744432L;
- private Config config = Model.config; // Passes the Model's configuration parameters object to a private field
-
public HouseSaleMarket() {
offersPY = new PriorityQueue2D<>(new HousingMarketRecord.PYComparator());
}
diff --git a/src/main/java/housing/Household.java b/src/main/java/housing/Household.java
index a8655f2d..f0e34c25 100644
--- a/src/main/java/housing/Household.java
+++ b/src/main/java/housing/Household.java
@@ -25,8 +25,6 @@ public class Household implements IHouseOwner, Serializable {
private static final long serialVersionUID = -5042897399316333745L;
- private Config config = Model.config; // Passes the Model's configuration parameters object to a private field
-
public static int bankruptcies = 0; // TODO: Unused variable... counts bankruptcies, but it's never used!
/********************************************************
diff --git a/src/main/java/housing/HouseholdBehaviour.java b/src/main/java/housing/HouseholdBehaviour.java
index d1393bbe..6fc76c1c 100644
--- a/src/main/java/housing/HouseholdBehaviour.java
+++ b/src/main/java/housing/HouseholdBehaviour.java
@@ -2,6 +2,7 @@
import java.io.Serializable;
+import configuration.HouseholdBehaviorConfig;
import org.apache.commons.math3.distribution.LogNormalDistribution;
//import ec.util.MersenneTwisterFast;
@@ -12,19 +13,18 @@
* @author daniel
*/
public class HouseholdBehaviour implements Serializable {// implements IHouseholdBehaviour {
- private static final long serialVersionUID = -7785886649432814279L;
-
- private Config config = Model.config; // Passes the Model's configuration parameters object to a private field
+ private static final long serialVersionUID = -7785886649432814279L;
+ private HouseholdBehaviorConfig config;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// public final double DOWNPAYMENT_FRACTION = 0.75 + 0.0025*rand.nextGaussian(); // Fraction of bank-balance household would like to spend on mortgage downpayments
-// public final double INTENSITY_OF_CHOICE = 10.0;
+// public final double DOWNPAYMENT_FRACTION = 0.75 + 0.0025*rand.nextGaussian(); // Fraction of bank-balance household would like to spend on mortgage downpayments
+// public final double INTENSITY_OF_CHOICE = 10.0;
- private Model.MersenneTwister rand = Model.rand; // Passes the Model's random number generator to a private field
- public boolean BTLInvestor;
- public double propensityToSave;
- public double desiredBalance;
- public double BtLCapGainCoeff; // Sensitivity of BtL investors to capital gain, 0.0 cares only about rental yield, 1.0 cares only about cap gain
+ private Model.MersenneTwister rand = Model.rand; // Passes the Model's random number generator to a private field
+ public boolean BTLInvestor;
+ public double propensityToSave;
+ public double desiredBalance;
+ public double BtLCapGainCoeff; // Sensitivity of BtL investors to capital gain, 0.0 cares only about rental yield, 1.0 cares only about cap gain
// Size distributions for downpayments of first-time-buyers and owner-occupiers
public LogNormalDistribution FTB_DOWNPAYMENT = new LogNormalDistribution(rand, config.DOWNPAYMENT_FTB_SCALE,
@@ -32,234 +32,237 @@ public class HouseholdBehaviour implements Serializable {// implements IHousehol
public LogNormalDistribution OO_DOWNPAYMENT = new LogNormalDistribution(rand, config.DOWNPAYMENT_OO_SCALE,
config.DOWNPAYMENT_OO_SHAPE);
- public double sigma(double x) { // the Logistic function, sometimes called sigma function, 1/1+e^(-x)
- return 1.0/(1.0+Math.exp(-1.0*x));
- }
-
- /***************************************
- * Constructor: initialise the behavioural variables for a new household: propensity to save, and
- * if the income percentile is above a minimum, decide whether to give the household
- * the BTL investor 'gene', and if so, decide whether they will be a fundamentalist or trend follower investor
- *
- * @param incomePercentile the fixed income percentile for the household (assumed constant over a lifetime),
- * used to determine whether the household can be a BTL investor
+ public double sigma(double x) { // the Logistic function, sometimes called sigma function, 1/1+e^(-x)
+ return 1.0/(1.0+Math.exp(-1.0*x));
+ }
+
+ /***************************************
+ * Constructor: initialise the behavioural variables for a new household: propensity to save, and
+ * if the income percentile is above a minimum, decide whether to give the household
+ * the BTL investor 'gene', and if so, decide whether they will be a fundamentalist or trend follower investor
+ *
+ * @param incomePercentile the fixed income percentile for the household (assumed constant over a lifetime),
+ * used to determine whether the household can be a BTL investor
***************************************************/
- public HouseholdBehaviour(double incomePercentile) {
- // Propensity to save is computed here so that it is constant for a given agent
- propensityToSave = config.DESIRED_BANK_BALANCE_EPSILON*rand.nextGaussian();
- BtLCapGainCoeff = 0.0;
- if(config.BTL_ENABLED) {
- if(incomePercentile > config.MIN_INVESTOR_PERCENTILE &&
- rand.nextDouble() < config.getPInvestor()/config.MIN_INVESTOR_PERCENTILE) {
- BTLInvestor = true;//(data.Households.buyToLetDistribution.inverseCumulativeProbability(rand.nextDouble())+0.5);
- double type = rand.nextDouble();
- if(type < config.P_FUNDAMENTALIST) {
- BtLCapGainCoeff = config.FUNDAMENTALIST_CAP_GAIN_COEFF;
- } else {
- BtLCapGainCoeff = config.TREND_CAP_GAIN_COEFF;
- }
- } else {
- BTLInvestor = false;
- }
- } else {
- BTLInvestor = false;
- }
- desiredBalance = -1.0;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Owner-Ocupier behaviour
- ///////////////////////////////////////////////////////////////////////////////////////////////
-
- /********************************
- * How much a household consumes (optional, non-essential consumption)
- * Consumption rule made to fit ONS wealth in Great Britain data.
- *
- * @param me Household
- * @return Non-essential consumption for the month
- ********************************/
- public double desiredConsumptionB(Household me) {//double monthlyIncome, double bankBalance) {
- return(config.CONSUMPTION_FRACTION*Math.max(me.getBankBalance() - desiredBankBalance(me), 0.0));
- }
-
- /********************************
- * @param me Household
- * @return Minimum bank balance a household would like to have at the end of the month (like a minimum safety net).
- * Used to determine non-essential consumption.
+ public HouseholdBehaviour(HouseholdBehaviorConfig config, double incomePercentile) {
+ this.config = config;
+
+ // Propensity to save is computed here so that it is constant for a given agent
+ propensityToSave = config.getDesiredBankBalanceConfig().getEpsilon()*rand.nextGaussian();
+ BtLCapGainCoeff = 0.0;
+ if(config.BTL_ENABLED) {
+ if(incomePercentile > config.getBuyToLetConfig().getMinIncomePercentile() &&
+ rand.nextDouble() < config.getBuyToLetConfig().getProbabilityInvestor() / config.getBuyToLetConfig().getMinIncomePercentile()) {
+ BTLInvestor = true;//(data.Households.buyToLetDistribution.inverseCumulativeProbability(rand.nextDouble())+0.5);
+ double type = rand.nextDouble();
+ if(type < config.getBuyToLetConfig().getProbabilityFundamentalist()) {
+ BtLCapGainCoeff = config.getBuyToLetConfig().getFundamentalistCapitalGainCoefficient();
+ } else {
+ BtLCapGainCoeff = config.getBuyToLetConfig().getTrendFollowersCapitalGainCoefficient();
+ }
+ } else {
+ BTLInvestor = false;
+ }
+ } else {
+ BTLInvestor = false;
+ }
+ desiredBalance = -1.0;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Owner-Ocupier behaviour
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ /********************************
+ * How much a household consumes (optional, non-essential consumption)
+ * Consumption rule made to fit ONS wealth in Great Britain data.
+ *
+ * @param me Household
+ * @return Non-essential consumption for the month
+ ********************************/
+ public double desiredConsumptionB(Household me) {//double monthlyIncome, double bankBalance) {
+ return(config.getConsumptionDecisionRuleConfig().getConsumptionFraction()*Math.max(me.getBankBalance() - desiredBankBalance(me), 0.0));
+ }
+
+ /********************************
+ * @param me Household
+ * @return Minimum bank balance a household would like to have at the end of the month (like a minimum safety net).
+ * Used to determine non-essential consumption.
*********************************/
- public double desiredBankBalance(Household me) {
+ public double desiredBankBalance(Household me) {
// TODO: why only if desired bank balance is set to -1? (does this get calculated only once? why?)
- if(desiredBalance == -1.0) {
-// desiredBalance = 3.0*Math.exp(4.07*Math.log(me.getMonthlyPreTaxIncome()*12.0)-33.1 - propensityToSave);
- double lnDesiredBalance = config.DESIRED_BANK_BALANCE_ALPHA
- + config.DESIRED_BANK_BALANCE_BETA
+ if(desiredBalance == -1.0) {
+// desiredBalance = 3.0*Math.exp(4.07*Math.log(me.getMonthlyPreTaxIncome()*12.0)-33.1 - propensityToSave);
+ double lnDesiredBalance = config.getDesiredBankBalanceConfig().getAlpha()
+ + config.getDesiredBankBalanceConfig().getBeta()
* Math.log(me.getMonthlyPreTaxIncome()*config.constants.MONTHS_IN_YEAR) + propensityToSave;
- desiredBalance = Math.exp(lnDesiredBalance);
- // TODO: What is this next rule? Not declared in the article! Check if 0.3 should be included as a parameter
- if(me.lifecycle.incomePercentile < 0.3 && !isPropertyInvestor()) desiredBalance = 1.0;
- }
- return(desiredBalance);
- }
-
- /***************************
- * @param me Household
- * @param monthlyIncome Mohthly income
- * @return desired purchase price after having decided to buy a house
- ****************************/
- public double desiredPurchasePrice(Household me, double monthlyIncome) {
- return(config.BUY_SCALE*config.constants.MONTHS_IN_YEAR*monthlyIncome
- *Math.exp(config.BUY_EPSILON*rand.nextGaussian())
- /(1.0 - config.BUY_WEIGHT_HPA*HPAExpectation()));
-
-// PurchasePlan plan = findBestPurchase(me);
-// double housePrice = Model.housingMarket.getAverageSalePrice(plan.quality);//behaviour.desiredPurchasePrice(getMonthlyPreTaxIncome(), houseMarket.housePriceAppreciation());
-// return(1.01*housePrice*Math.exp(0.05*rand.nextGaussian()));
- }
-
- /********************************
- * @param pbar average sale price of houses of the same quality
- * @param d average number of days on the market before sale // TODO: Is this average or for this property
- * @param principal amount of principal left on any mortgage on this house
- * @return initial sale price of a house
- ********************************/
- public double initialSalePrice(double pbar, double d, double principal) {
- // TODO: During the first month, the third term is actually introducing an extra markup. Solve!
- double exponent = config.SALE_MARKUP + Math.log(pbar)
+ desiredBalance = Math.exp(lnDesiredBalance);
+ // TODO: What is this next rule? Not declared in the article! Check if 0.3 should be included as a parameter
+ if(me.lifecycle.incomePercentile < 0.3 && !isPropertyInvestor()) desiredBalance = 1.0;
+ }
+ return(desiredBalance);
+ }
+
+ /***************************
+ * @param me Household
+ * @param monthlyIncome Mohthly income
+ * @return desired purchase price after having decided to buy a house
+ ****************************/
+ public double desiredPurchasePrice(Household me, double monthlyIncome) {
+ return(config.BUY_SCALE*config.constants.MONTHS_IN_YEAR*monthlyIncome
+ *Math.exp(config.BUY_EPSILON*rand.nextGaussian())
+ /(1.0 - config.BUY_WEIGHT_HPA*HPAExpectation()));
+
+// PurchasePlan plan = findBestPurchase(me);
+// double housePrice = Model.housingMarket.getAverageSalePrice(plan.quality);//behaviour.desiredPurchasePrice(getMonthlyPreTaxIncome(), houseMarket.housePriceAppreciation());
+// return(1.01*housePrice*Math.exp(0.05*rand.nextGaussian()));
+ }
+
+ /********************************
+ * @param pbar average sale price of houses of the same quality
+ * @param d average number of days on the market before sale // TODO: Is this average or for this property
+ * @param principal amount of principal left on any mortgage on this house
+ * @return initial sale price of a house
+ ********************************/
+ public double initialSalePrice(double pbar, double d, double principal) {
+ // TODO: During the first month, the third term is actually introducing an extra markup. Solve!
+ double exponent = config.SALE_MARKUP + Math.log(pbar)
- config.SALE_WEIGHT_DAYS_ON_MARKET*Math.log((d + 1.0)/(config.constants.DAYS_IN_MONTH + 1.0))
+ config.SALE_EPSILON*rand.nextGaussian();
- return(Math.max(Math.exp(exponent), principal));
- }
-
-
- /**
- * @return Does an owner-occupier decide to sell house?
- */
- public boolean decideToSellHome(Household me) {
- // TODO: need to add expenditure
- if(isPropertyInvestor()) return(false);
- return(rand.nextDouble() < config.derivedParams.MONTHLY_P_SELL*(1.0
+ return(Math.max(Math.exp(exponent), principal));
+ }
+
+
+ /**
+ * @return Does an owner-occupier decide to sell house?
+ */
+ public boolean decideToSellHome(Household me) {
+ // TODO: need to add expenditure
+ if(isPropertyInvestor()) return(false);
+ return(rand.nextDouble() < config.derivedParams.MONTHLY_P_SELL*(1.0
+ config.DECISION_TO_SELL_ALPHA*(config.DECISION_TO_SELL_HPC
- Model.housingMarket.offersPQ.size()*1.0/Model.households.size()))
+ config.DECISION_TO_SELL_BETA*(config.DECISION_TO_SELL_INTEREST - Model.bank.getMortgageInterestRate()));
- // reference
- //int potentialQualityChange = Model.housingMarket.maxQualityGivenPrice(Model.bank.getMaxMortgage(me,true))- me.home.getQuality();
- //double p_move = data.Households.P_FORCEDTOMOVE + (data.Households.P_SELL-data.Households.P_FORCEDTOMOVE)/(1.0+Math.exp(5.0-2.0*potentialQualityChange));
-
- /*
-
- // calc purchase price
- PurchasePlan plan = findBestPurchase(me);
- if(plan.quality < 0) return(false); // can't afford new home anyway
- int currentQuality = me.home.getQuality();
- double currentUtility;// = utilityOfHome(me,me.home.getQuality()) - me.mortgageFor(me.home).nextPayment()/me.getMonthlyPreTaxIncome();
-// currentUtility = utilityOfHome(me,currentQuality) +(Model.housingMarket.getAverageSalePrice(currentQuality)*HPAExpectation()/12.0 - me.mortgageFor(me.home).nextPayment())/me.getMonthlyPreTaxIncome();
- double currentLeftForConsumption = 1.0 - (me.mortgageFor(me.home).nextPayment() - Model.housingMarket.getAverageSalePrice(currentQuality)*HPAExpectation()/12.0)/me.monthlyEmploymentIncome;
-// currentUtility = (currentQuality-me.desiredQuality)/House.Config.N_QUALITY + qualityOfLiving(currentLeftForConsumption);
- currentUtility = utilityOfHome(me, currentQuality) + qualityOfLiving(currentLeftForConsumption);
-// System.out.println("Move utility = "+(plan.utility- currentUtility));
-
- double p_move = data.Households.P_FORCEDTOMOVE;
- p_move += 2.0*(data.Households.P_SELL-data.Households.P_FORCEDTOMOVE)/(1.0+Math.exp(4.0-INTENSITY_OF_CHOICE*(plan.utility - currentUtility)));
- p_move *= 1.0 - data.HouseSaleMarket.SEASONAL_VOL_ADJ*Math.cos((2.0*3.141/12.0)*Model.getMonth());
- // System.out.println("Move utility = "+INTENSITY_OF_CHOICE*(plan.utility- currentUtility)+" "+p_move);
- return(rand.nextDouble() < p_move);
- */
- }
-
- /**
- *
- * @param me the household
- * @param housePrice the price of the house
+ // reference
+ //int potentialQualityChange = Model.housingMarket.maxQualityGivenPrice(Model.bank.getMaxMortgage(me,true))- me.home.getQuality();
+ //double p_move = data.Households.P_FORCEDTOMOVE + (data.Households.P_SELL-data.Households.P_FORCEDTOMOVE)/(1.0+Math.exp(5.0-2.0*potentialQualityChange));
+
+ /*
+
+ // calc purchase price
+ PurchasePlan plan = findBestPurchase(me);
+ if(plan.quality < 0) return(false); // can't afford new home anyway
+ int currentQuality = me.home.getQuality();
+ double currentUtility;// = utilityOfHome(me,me.home.getQuality()) - me.mortgageFor(me.home).nextPayment()/me.getMonthlyPreTaxIncome();
+// currentUtility = utilityOfHome(me,currentQuality) +(Model.housingMarket.getAverageSalePrice(currentQuality)*HPAExpectation()/12.0 - me.mortgageFor(me.home).nextPayment())/me.getMonthlyPreTaxIncome();
+ double currentLeftForConsumption = 1.0 - (me.mortgageFor(me.home).nextPayment() - Model.housingMarket.getAverageSalePrice(currentQuality)*HPAExpectation()/12.0)/me.monthlyEmploymentIncome;
+// currentUtility = (currentQuality-me.desiredQuality)/House.Config.N_QUALITY + qualityOfLiving(currentLeftForConsumption);
+ currentUtility = utilityOfHome(me, currentQuality) + qualityOfLiving(currentLeftForConsumption);
+// System.out.println("Move utility = "+(plan.utility- currentUtility));
+
+ double p_move = data.Households.P_FORCEDTOMOVE;
+ p_move += 2.0*(data.Households.P_SELL-data.Households.P_FORCEDTOMOVE)/(1.0+Math.exp(4.0-INTENSITY_OF_CHOICE*(plan.utility - currentUtility)));
+ p_move *= 1.0 - data.HouseSaleMarket.SEASONAL_VOL_ADJ*Math.cos((2.0*3.141/12.0)*Model.getMonth());
+ // System.out.println("Move utility = "+INTENSITY_OF_CHOICE*(plan.utility- currentUtility)+" "+p_move);
+ return(rand.nextDouble() < p_move);
+ */
+ }
+
+ /**
+ *
+ * @param me the household
+ * @param housePrice the price of the house
* @return the downpayment
*/
- public double downPayment(Household me, double housePrice) {
-// return(me.getBankBalance() - (1.0 - DOWNPAYMENT_FRACTION)*desiredBankBalance(me));
- if(me.getBankBalance() > housePrice*config.BANK_BALANCE_FOR_CASH_DOWNPAYMENT) { // calibrated against mortgage approval/housing transaction ratio, core indicators average 1987-2006
- return(housePrice);
- }
- double downpayment;
- if(me.isFirstTimeBuyer()) {
- downpayment = Model.housingMarket.housePriceIndex*FTB_DOWNPAYMENT.inverseCumulativeProbability(Math.max(0.0,
+ public double downPayment(Household me, double housePrice) {
+// return(me.getBankBalance() - (1.0 - DOWNPAYMENT_FRACTION)*desiredBankBalance(me));
+ if(me.getBankBalance() > housePrice*config.BANK_BALANCE_FOR_CASH_DOWNPAYMENT) { // calibrated against mortgage approval/housing transaction ratio, core indicators average 1987-2006
+ return(housePrice);
+ }
+ double downpayment;
+ if(me.isFirstTimeBuyer()) {
+ downpayment = Model.housingMarket.housePriceIndex*FTB_DOWNPAYMENT.inverseCumulativeProbability(Math.max(0.0,
(me.lifecycle.incomePercentile - config.DOWNPAYMENT_MIN_INCOME)/(1 - config.DOWNPAYMENT_MIN_INCOME)));
- } else if(isPropertyInvestor()) {
- downpayment = housePrice*(Math.max(0.0,
- config.DOWNPAYMENT_BTL_MEAN + config.DOWNPAYMENT_BTL_EPSILON*rand.nextGaussian())); // calibrated...
- //downpayment = housePrice*(Math.max(0.0, 0.26+0.08*rand.nextGaussian())); // calibrated...
- } else {
- downpayment = Model.housingMarket.housePriceIndex*OO_DOWNPAYMENT.inverseCumulativeProbability(Math.max(0.0,
+ } else if(isPropertyInvestor()) {
+ downpayment = housePrice*(Math.max(0.0,
+ config.DOWNPAYMENT_BTL_MEAN + config.DOWNPAYMENT_BTL_EPSILON*rand.nextGaussian())); // calibrated...
+ //downpayment = housePrice*(Math.max(0.0, 0.26+0.08*rand.nextGaussian())); // calibrated...
+ } else {
+ downpayment = Model.housingMarket.housePriceIndex*OO_DOWNPAYMENT.inverseCumulativeProbability(Math.max(0.0,
(me.lifecycle.incomePercentile - config.DOWNPAYMENT_MIN_INCOME)/(1 - config.DOWNPAYMENT_MIN_INCOME)));
- }
- if(downpayment > me.getBankBalance()) downpayment = me.getBankBalance();
- return(downpayment);
-// return(Model.housingMarket.housePriceIndex*OO_DOWNPAYMENT.inverseCumulativeProbability(me.lifecycle.incomePercentile));
- }
-
-
- /********************************************************
- * Decide how much to drop the list-price of a house if
- * it has been on the market for (another) month and hasn't
- * sold. Calibrated against Zoopla dataset in Bank of England
- *
- * @param sale The HouseSaleRecord of the house that is on the market.
- ********************************************************/
- public double rethinkHouseSalePrice(HouseSaleRecord sale) {
-// return(sale.getPrice() *0.95);
-
- if(rand.nextDouble() < config.P_SALE_PRICE_REDUCE) {
- double logReduction = config.REDUCTION_MU+(rand.nextGaussian()*config.REDUCTION_SIGMA);
-// System.out.println(1.0-Math.exp(logReduction)/100.0);
- return(sale.getPrice() * (1.0-Math.exp(logReduction)/100.0));
-// return(sale.getPrice() * (1.0-data.Households.REDUCTION_MU/100.0) + rand.nextGaussian()*data.Households.REDUCTION_SIGMA/100.0);
- }
- return(sale.getPrice());
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Renter behaviour
- ///////////////////////////////////////////////////////////////////////////////////////////////
-
- /*** renters or OO after selling home decide whether to rent or buy
- * N.B. even though the HH may not decide to rent a house of the
- * same quality as they would buy, the cash value of the difference in quality
- * is assumed to be the difference in rental price between the two qualities.
- * @return true if we should buy a house, false if we should rent
- */
- public boolean rentOrPurchaseDecision(Household me) {
- if(isPropertyInvestor()) return(true);
-
- double purchasePrice = Math.min(desiredPurchasePrice(me, me.monthlyEmploymentIncome),
+ }
+ if(downpayment > me.getBankBalance()) downpayment = me.getBankBalance();
+ return(downpayment);
+// return(Model.housingMarket.housePriceIndex*OO_DOWNPAYMENT.inverseCumulativeProbability(me.lifecycle.incomePercentile));
+ }
+
+
+ /********************************************************
+ * Decide how much to drop the list-price of a house if
+ * it has been on the market for (another) month and hasn't
+ * sold. Calibrated against Zoopla dataset in Bank of England
+ *
+ * @param sale The HouseSaleRecord of the house that is on the market.
+ ********************************************************/
+ public double rethinkHouseSalePrice(HouseSaleRecord sale) {
+// return(sale.getPrice() *0.95);
+
+ if(rand.nextDouble() < config.P_SALE_PRICE_REDUCE) {
+ double logReduction = config.REDUCTION_MU+(rand.nextGaussian()*config.REDUCTION_SIGMA);
+// System.out.println(1.0-Math.exp(logReduction)/100.0);
+ return(sale.getPrice() * (1.0-Math.exp(logReduction)/100.0));
+// return(sale.getPrice() * (1.0-data.Households.REDUCTION_MU/100.0) + rand.nextGaussian()*data.Households.REDUCTION_SIGMA/100.0);
+ }
+ return(sale.getPrice());
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Renter behaviour
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ /*** renters or OO after selling home decide whether to rent or buy
+ * N.B. even though the HH may not decide to rent a house of the
+ * same quality as they would buy, the cash value of the difference in quality
+ * is assumed to be the difference in rental price between the two qualities.
+ * @return true if we should buy a house, false if we should rent
+ */
+ public boolean rentOrPurchaseDecision(Household me) {
+ if(isPropertyInvestor()) return(true);
+
+ double purchasePrice = Math.min(desiredPurchasePrice(me, me.monthlyEmploymentIncome),
Model.bank.getMaxMortgage(me, true));
- MortgageAgreement mortgageApproval = Model.bank.requestApproval(me, purchasePrice,
+ MortgageAgreement mortgageApproval = Model.bank.requestApproval(me, purchasePrice,
downPayment(me,purchasePrice), true);
- int newHouseQuality = Model.housingMarket.maxQualityGivenPrice(purchasePrice);
-// int rentalQuality = Model.rentalMarket.maxQualityGivenPrice(desiredRent(me, me.monthlyEmploymentIncome));
-// if(rentalQuality > newHouseQuality+House.Config.N_QUALITY/8) return(false); // better quality to rent
- if(newHouseQuality < 0) return(false); // can't afford a house anyway
- double costOfHouse = mortgageApproval.monthlyPayment*config.constants.MONTHS_IN_YEAR - purchasePrice*HPAExpectation();
- double costOfRent = Model.rentalMarket.getAverageSalePrice(newHouseQuality)*config.constants.MONTHS_IN_YEAR;
-// System.out.println(FTB_K*(costOfRent + COST_OF_RENTING - costOfHouse));
- //return(rand.nextDouble() < 1.0/(1.0 + Math.exp(-FTB_K*(costOfRent*(1.0+COST_OF_RENTING) - costOfHouse))));
- return(rand.nextDouble() < sigma(config.SENSITIVITY_RENT_OR_PURCHASE*(costOfRent*(1.0
+ int newHouseQuality = Model.housingMarket.maxQualityGivenPrice(purchasePrice);
+// int rentalQuality = Model.rentalMarket.maxQualityGivenPrice(desiredRent(me, me.monthlyEmploymentIncome));
+// if(rentalQuality > newHouseQuality+House.Config.N_QUALITY/8) return(false); // better quality to rent
+ if(newHouseQuality < 0) return(false); // can't afford a house anyway
+ double costOfHouse = mortgageApproval.monthlyPayment*config.constants.MONTHS_IN_YEAR - purchasePrice*HPAExpectation();
+ double costOfRent = Model.rentalMarket.getAverageSalePrice(newHouseQuality)*config.constants.MONTHS_IN_YEAR;
+// System.out.println(FTB_K*(costOfRent + COST_OF_RENTING - costOfHouse));
+ //return(rand.nextDouble() < 1.0/(1.0 + Math.exp(-FTB_K*(costOfRent*(1.0+COST_OF_RENTING) - costOfHouse))));
+ return(rand.nextDouble() < sigma(config.SENSITIVITY_RENT_OR_PURCHASE*(costOfRent*(1.0
+ config.PSYCHOLOGICAL_COST_OF_RENTING) - costOfHouse)));
- /*
-
- PurchasePlan purchase = findBestPurchase(me);
- if(purchase.quality == 0 && purchase.utility <-10.0) return(false); // can't afford to buy anyway
- int rentQuality = findBestRentalQuality(me);
-
- if(me.isFirstTimeBuyer()) {
- COST_OF_RENTING = 0.001;
- } else {
- COST_OF_RENTING = 0.01;
- }
-
- double pBuy = 1.0/(1.0 + Math.exp(-INTENSITY_OF_CHOICE*(COST_OF_RENTING + purchase.utility - utilityOfRenting(me, rentQuality))));
-// System.out.println(utilityOfRenting(me, rentQuality) + " : "+purchase.utility+" : "+INTENSITY_OF_CHOICE*(COST_OF_RENTING+purchase.utility-utilityOfRenting(me, rentQuality))+" ... "+pBuy);
- return(rand.nextDouble() < pBuy);
- */
-
- }
+ /*
+
+ PurchasePlan purchase = findBestPurchase(me);
+ if(purchase.quality == 0 && purchase.utility <-10.0) return(false); // can't afford to buy anyway
+ int rentQuality = findBestRentalQuality(me);
+
+ if(me.isFirstTimeBuyer()) {
+ COST_OF_RENTING = 0.001;
+ } else {
+ COST_OF_RENTING = 0.01;
+ }
+
+ double pBuy = 1.0/(1.0 + Math.exp(-INTENSITY_OF_CHOICE*(COST_OF_RENTING + purchase.utility - utilityOfRenting(me, rentQuality))));
+// System.out.println(utilityOfRenting(me, rentQuality) + " : "+purchase.utility+" : "+INTENSITY_OF_CHOICE*(COST_OF_RENTING+purchase.utility-utilityOfRenting(me, rentQuality))+" ... "+pBuy);
+ return(rand.nextDouble() < pBuy);
+ */
+
+ }
public boolean rentOrPurchaseDecision(Household me, double desiredPurchasePrice) {
if(isPropertyInvestor()) return(true);
@@ -268,266 +271,266 @@ public boolean rentOrPurchaseDecision(Household me, double desiredPurchasePrice)
MortgageAgreement mortgageApproval = Model.bank.requestApproval(me, purchasePrice,
downPayment(me,purchasePrice), true);
int newHouseQuality = Model.housingMarket.maxQualityGivenPrice(purchasePrice);
-// int rentalQuality = Model.rentalMarket.maxQualityGivenPrice(desiredRent(me, me.monthlyEmploymentIncome));
-// if(rentalQuality > newHouseQuality+House.Config.N_QUALITY/8) return(false); // better quality to rent
+// int rentalQuality = Model.rentalMarket.maxQualityGivenPrice(desiredRent(me, me.monthlyEmploymentIncome));
+// if(rentalQuality > newHouseQuality+House.Config.N_QUALITY/8) return(false); // better quality to rent
if(newHouseQuality < 0) return(false); // can't afford a house anyway
double costOfHouse = mortgageApproval.monthlyPayment*config.constants.MONTHS_IN_YEAR - purchasePrice*HPAExpectation();
double costOfRent = Model.rentalMarket.getAverageSalePrice(newHouseQuality)*config.constants.MONTHS_IN_YEAR;
-// System.out.println(FTB_K*(costOfRent + COST_OF_RENTING - costOfHouse));
+// System.out.println(FTB_K*(costOfRent + COST_OF_RENTING - costOfHouse));
//return(rand.nextDouble() < 1.0/(1.0 + Math.exp(-FTB_K*(costOfRent*(1.0+COST_OF_RENTING) - costOfHouse))));
return(rand.nextDouble() < sigma(config.SENSITIVITY_RENT_OR_PURCHASE*(costOfRent*(1.0
+ config.PSYCHOLOGICAL_COST_OF_RENTING) - costOfHouse)));
- /*
+ /*
- PurchasePlan purchase = findBestPurchase(me);
- if(purchase.quality == 0 && purchase.utility <-10.0) return(false); // can't afford to buy anyway
- int rentQuality = findBestRentalQuality(me);
+ PurchasePlan purchase = findBestPurchase(me);
+ if(purchase.quality == 0 && purchase.utility <-10.0) return(false); // can't afford to buy anyway
+ int rentQuality = findBestRentalQuality(me);
- if(me.isFirstTimeBuyer()) {
- COST_OF_RENTING = 0.001;
- } else {
- COST_OF_RENTING = 0.01;
- }
+ if(me.isFirstTimeBuyer()) {
+ COST_OF_RENTING = 0.001;
+ } else {
+ COST_OF_RENTING = 0.01;
+ }
- double pBuy = 1.0/(1.0 + Math.exp(-INTENSITY_OF_CHOICE*(COST_OF_RENTING + purchase.utility - utilityOfRenting(me, rentQuality))));
-// System.out.println(utilityOfRenting(me, rentQuality) + " : "+purchase.utility+" : "+INTENSITY_OF_CHOICE*(COST_OF_RENTING+purchase.utility-utilityOfRenting(me, rentQuality))+" ... "+pBuy);
- return(rand.nextDouble() < pBuy);
- */
+ double pBuy = 1.0/(1.0 + Math.exp(-INTENSITY_OF_CHOICE*(COST_OF_RENTING + purchase.utility - utilityOfRenting(me, rentQuality))));
+// System.out.println(utilityOfRenting(me, rentQuality) + " : "+purchase.utility+" : "+INTENSITY_OF_CHOICE*(COST_OF_RENTING+purchase.utility-utilityOfRenting(me, rentQuality))+" ... "+pBuy);
+ return(rand.nextDouble() < pBuy);
+ */
}
- /********************************************************
- * Decide how much to bid on the rental market
- * Source: Zoopla rental prices 2008-2009 (at Bank of England)
- ********************************************************/
- public double desiredRent(Household me, double monthlyIncome) {
- return(monthlyIncome * config.DESIRED_RENT_INCOME_FRACTION);
-
-// int quality = findBestRentalQuality(me);
-// return(1.01*Model.rentalMarket.getAverageSalePrice(quality)*Math.exp(0.1*rand.nextGaussian()));
-
- /*
- // Zoopla calibrated values
- double annualIncome = monthlyIncome*12.0; // this should be net annual income, not gross
- double rent;
- if(annualIncome < 12000.0) {
- rent = 386.0;
- } else {
- rent = 11.72*Math.pow(annualIncome, 0.372);
- }
- rent *= Math.exp(rand.nextGaussian()*0.0826);
- return(rent);
- */
- }
-
- ///////////////////////////////////////////////////////////////////////////////////////////////
- // Property investor behaviour
- ///////////////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * Decide whether to sell an investment property.
- *
- * @param h The house in question
- * @param me The investor
- * @return Does an investor decide to sell a buy-to-let property (per month)
- */
- public boolean decideToSellInvestmentProperty(House h, Household me) {
- if(me.nInvestmentProperties() < 2) return(false); // Always keep at least one property
-
- double effectiveYield;
-
- // sell if not selling on rental market at interest coverage ratio of 1.0 (?)
- if(!h.isOnRentalMarket()) return(false); // don't sell while occupied by tenant
- MortgageAgreement mortgage = me.mortgageFor(h);
- //if(mortgage == null) {
- if(h.owner!=me){
- System.out.println("Strange: deciding to sell investment property that I don't own");
- return(false);
- }
- // TODO: add transaction costs to expected capital gain
-// double icr = (h.rentalRecord.getPrice()-mortgage.nextPayment())/h.rentalRecord.getPrice();
- double marketPrice = Model.housingMarket.getAverageSalePrice(h.getQuality());
- // TODO: Why to call this "equity"? It is called "downpayment" in the article!
+ /********************************************************
+ * Decide how much to bid on the rental market
+ * Source: Zoopla rental prices 2008-2009 (at Bank of England)
+ ********************************************************/
+ public double desiredRent(Household me, double monthlyIncome) {
+ return(monthlyIncome * config.DESIRED_RENT_INCOME_FRACTION);
+
+// int quality = findBestRentalQuality(me);
+// return(1.01*Model.rentalMarket.getAverageSalePrice(quality)*Math.exp(0.1*rand.nextGaussian()));
+
+ /*
+ // Zoopla calibrated values
+ double annualIncome = monthlyIncome*12.0; // this should be net annual income, not gross
+ double rent;
+ if(annualIncome < 12000.0) {
+ rent = 386.0;
+ } else {
+ rent = 11.72*Math.pow(annualIncome, 0.372);
+ }
+ rent *= Math.exp(rand.nextGaussian()*0.0826);
+ return(rent);
+ */
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // Property investor behaviour
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Decide whether to sell an investment property.
+ *
+ * @param h The house in question
+ * @param me The investor
+ * @return Does an investor decide to sell a buy-to-let property (per month)
+ */
+ public boolean decideToSellInvestmentProperty(House h, Household me) {
+ if(me.nInvestmentProperties() < 2) return(false); // Always keep at least one property
+
+ double effectiveYield;
+
+ // sell if not selling on rental market at interest coverage ratio of 1.0 (?)
+ if(!h.isOnRentalMarket()) return(false); // don't sell while occupied by tenant
+ MortgageAgreement mortgage = me.mortgageFor(h);
+ //if(mortgage == null) {
+ if(h.owner!=me){
+ System.out.println("Strange: deciding to sell investment property that I don't own");
+ return(false);
+ }
+ // TODO: add transaction costs to expected capital gain
+// double icr = (h.rentalRecord.getPrice()-mortgage.nextPayment())/h.rentalRecord.getPrice();
+ double marketPrice = Model.housingMarket.getAverageSalePrice(h.getQuality());
+ // TODO: Why to call this "equity"? It is called "downpayment" in the article!
double equity = Math.max(0.01, marketPrice - mortgage.principal); // Dummy security parameter to avoid dividing by zero
- double leverage = marketPrice/equity;
- double rentalYield = h.rentalRecord.getPrice()*config.constants.MONTHS_IN_YEAR/marketPrice;
- double mortgageRate = mortgage.nextPayment()*config.constants.MONTHS_IN_YEAR/equity;
- if(config.BTL_YIELD_SCALING) {
- effectiveYield = leverage*((1.0 - BtLCapGainCoeff)*rentalYield
+ double leverage = marketPrice/equity;
+ double rentalYield = h.rentalRecord.getPrice()*config.constants.MONTHS_IN_YEAR/marketPrice;
+ double mortgageRate = mortgage.nextPayment()*config.constants.MONTHS_IN_YEAR/equity;
+ if(config.BTL_YIELD_SCALING) {
+ effectiveYield = leverage*((1.0 - BtLCapGainCoeff)*rentalYield
+ BtLCapGainCoeff*(Model.rentalMarket.longTermAverageGrossYield + HPAExpectation())) - mortgageRate;
- } else {
- effectiveYield = leverage*(rentalYield + BtLCapGainCoeff*HPAExpectation()) - mortgageRate;
- }
- double pKeep = Math.pow(sigma(config.BTL_CHOICE_INTENSITY*effectiveYield),
+ } else {
+ effectiveYield = leverage*(rentalYield + BtLCapGainCoeff*HPAExpectation()) - mortgageRate;
+ }
+ double pKeep = Math.pow(sigma(config.BTL_CHOICE_INTENSITY*effectiveYield),
1.0/config.constants.MONTHS_IN_YEAR);
- return(rand.nextDouble() < (1.0 - pKeep));
- }
-
-
- /**
- * How much rent does an investor decide to charge on a buy-to-let house?
- * @param rbar average rent for house of this quality
- * @param d average days on market
- * @param h house being offered for rent
- */
- public double buyToLetRent(double rbar, double d, House h) {
- // TODO: What? Where does this equation come from?
- final double beta = config.RENT_MARKUP/Math.log(config.RENT_EQ_MONTHS_ON_MARKET); // Weight of days-on-market effect
-
- double exponent = config.RENT_MARKUP + Math.log(rbar)
+ return(rand.nextDouble() < (1.0 - pKeep));
+ }
+
+
+ /**
+ * How much rent does an investor decide to charge on a buy-to-let house?
+ * @param rbar average rent for house of this quality
+ * @param d average days on market
+ * @param h house being offered for rent
+ */
+ public double buyToLetRent(double rbar, double d, House h) {
+ // TODO: What? Where does this equation come from?
+ final double beta = config.RENT_MARKUP/Math.log(config.RENT_EQ_MONTHS_ON_MARKET); // Weight of days-on-market effect
+
+ double exponent = config.RENT_MARKUP + Math.log(rbar)
- beta*Math.log((d + 1.0)/(config.constants.DAYS_IN_MONTH + 1))
+ config.RENT_EPSILON*rand.nextGaussian();
- double result = Math.exp(exponent);
+ double result = Math.exp(exponent);
// TODO: The following contains a fudge (config.RENT_MAX_AMORTIZATION_PERIOD) to keep rental yield up
- double minAcceptable = Model.housingMarket.getAverageSalePrice(h.getQuality())
+ double minAcceptable = Model.housingMarket.getAverageSalePrice(h.getQuality())
/(config.RENT_MAX_AMORTIZATION_PERIOD*config.constants.MONTHS_IN_YEAR);
- if(result < minAcceptable) result = minAcceptable;
- return(result);
+ if(result < minAcceptable) result = minAcceptable;
+ return(result);
- }
+ }
+
+ /**
+ * Update the demanded rent for a property
+ *
+ * @param sale the HouseSaleRecord of the property for rent
+ * @return the new rent
+ */
+ public double rethinkBuyToLetRent(HouseSaleRecord sale) {
+ return((1.0 - config.RENT_REDUCTION)*sale.getPrice());
+// if(rand.nextDouble() > 0.944) {
+// double logReduction = Math.min(4.6, 1.603+(rand.nextGaussian()*0.6173));
+// return(sale.getPrice() * (1.0-0.01*Math.exp(logReduction)));
+// }
+// return(sale.getPrice());
+ }
- /**
- * Update the demanded rent for a property
- *
- * @param sale the HouseSaleRecord of the property for rent
- * @return the new rent
+ /***
+ * Monthly opportunity of buying a new BTL property.
+ *
+ * Investor households with no investment properties always attempt to buy one. Households with at least
+ * one investment property will calculate the expected yield of a new property based on two contributions:
+ * capital gain and rental yield (with their corresponding weights which depend on the type of investor).
+ *
+ * @param me household
+ * @return true if decision to buy
*/
- public double rethinkBuyToLetRent(HouseSaleRecord sale) {
- return((1.0 - config.RENT_REDUCTION)*sale.getPrice());
-// if(rand.nextDouble() > 0.944) {
-// double logReduction = Math.min(4.6, 1.603+(rand.nextGaussian()*0.6173));
-// return(sale.getPrice() * (1.0-0.01*Math.exp(logReduction)));
-// }
-// return(sale.getPrice());
- }
-
- /***
- * Monthly opportunity of buying a new BTL property.
- *
- * Investor households with no investment properties always attempt to buy one. Households with at least
- * one investment property will calculate the expected yield of a new property based on two contributions:
- * capital gain and rental yield (with their corresponding weights which depend on the type of investor).
- *
- * @param me household
- * @return true if decision to buy
- */
- public boolean decideToBuyBuyToLet(Household me) {
- if(me.nInvestmentProperties() < 1) { // If I don't have any BTL properties, I always decide to buy one!!
- return(true);
- }
- double effectiveYield;
-
- if(!isPropertyInvestor()) return false;
- // TODO: This mechanism and its parameter are not declared in the article! Any reference for the value of the parameter?
- if(me.getBankBalance() < desiredBankBalance(me)*config.BTL_CHOICE_MIN_BANK_BALANCE) {
- return(false);
- }
- // --- calculate expected yield on zero quality house
- double maxPrice = Model.bank.getMaxMortgage(me, false);
- if(maxPrice < Model.housingMarket.getAverageSalePrice(0)) return false;
-
- MortgageAgreement m = Model.bank.requestApproval(me, maxPrice, 0.0, false); // maximise leverage with min downpayment
-
- double leverage = m.purchasePrice/m.downPayment;
- double rentalYield = Model.rentalMarket.averageSoldGrossYield;
- double mortgageRate = m.monthlyPayment*config.constants.MONTHS_IN_YEAR/m.downPayment;
- if(config.BTL_YIELD_SCALING) {
- effectiveYield = leverage*((1.0 - BtLCapGainCoeff)*rentalYield
+ public boolean decideToBuyBuyToLet(Household me) {
+ if(me.nInvestmentProperties() < 1) { // If I don't have any BTL properties, I always decide to buy one!!
+ return(true);
+ }
+ double effectiveYield;
+
+ if(!isPropertyInvestor()) return false;
+ // TODO: This mechanism and its parameter are not declared in the article! Any reference for the value of the parameter?
+ if(me.getBankBalance() < desiredBankBalance(me)*config.BTL_CHOICE_MIN_BANK_BALANCE) {
+ return(false);
+ }
+ // --- calculate expected yield on zero quality house
+ double maxPrice = Model.bank.getMaxMortgage(me, false);
+ if(maxPrice < Model.housingMarket.getAverageSalePrice(0)) return false;
+
+ MortgageAgreement m = Model.bank.requestApproval(me, maxPrice, 0.0, false); // maximise leverage with min downpayment
+
+ double leverage = m.purchasePrice/m.downPayment;
+ double rentalYield = Model.rentalMarket.averageSoldGrossYield;
+ double mortgageRate = m.monthlyPayment*config.constants.MONTHS_IN_YEAR/m.downPayment;
+ if(config.BTL_YIELD_SCALING) {
+ effectiveYield = leverage*((1.0 - BtLCapGainCoeff)*rentalYield
+ BtLCapGainCoeff*(Model.rentalMarket.longTermAverageGrossYield + HPAExpectation())) - mortgageRate;
- } else {
- effectiveYield = leverage*(rentalYield + BtLCapGainCoeff*HPAExpectation()) - mortgageRate;
- }
- //double pDontBuy = Math.pow(1.0/(1.0 + Math.exp(INTENSITY*effectiveYield)),AGGREGATE_RATE);
- //return(rand.nextDouble() < (1.0-pDontBuy));
- return (rand.nextDouble() < Math.pow(sigma(config.BTL_CHOICE_INTENSITY*effectiveYield),
+ } else {
+ effectiveYield = leverage*(rentalYield + BtLCapGainCoeff*HPAExpectation()) - mortgageRate;
+ }
+ //double pDontBuy = Math.pow(1.0/(1.0 + Math.exp(INTENSITY*effectiveYield)),AGGREGATE_RATE);
+ //return(rand.nextDouble() < (1.0-pDontBuy));
+ return (rand.nextDouble() < Math.pow(sigma(config.BTL_CHOICE_INTENSITY*effectiveYield),
1.0/config.constants.MONTHS_IN_YEAR));
- }
-
- public double btlPurchaseBid(Household me) {
- return(Math.min(Model.bank.getMaxMortgage(me, false),
+ }
+
+ public double btlPurchaseBid(Household me) {
+ return(Math.min(Model.bank.getMaxMortgage(me, false),
1.1*Model.housingMarket.getAverageSalePrice(config.N_QUALITY-1)));
- }
+ }
+
+ public boolean isPropertyInvestor() {
+ return(BTLInvestor);
+ }
+
+ public boolean setPropertyInvestor(boolean isInvestor) {
+ return(BTLInvestor = isInvestor);
+ }
+
+// public int nDesiredBTLProperties() {
+// return desiredBTLProperties;
+// }
- public boolean isPropertyInvestor() {
- return(BTLInvestor);
- }
+ /*** @returns expectation value of HPI in one year's time divided by today's HPI*/
+ public double HPAExpectation() {
+ // Dampening or multiplier factor, depending on its value being <1 or >1, for the current trend of HPA when
+ // computing expectations as in HPI(t+DT) = HPI(t) + FACTOR*DT*dHPI/dt (double)
+ return(Model.housingMarket.housePriceAppreciation(config.HPA_YEARS_TO_CHECK)*config.HPA_EXPECTATION_FACTOR);
+ }
+
+ /*
+ public double utilityOfRenting(Household me, int q) {
+ double leftForConsumption = 1.0 - Model.rentalMarket.getAverageSalePrice(q)/me.monthlyEmploymentIncome;
+ return(utilityOfHome(me,q) + qualityOfLiving(leftForConsumption));
+ }
- public boolean setPropertyInvestor(boolean isInvestor) {
- return(BTLInvestor = isInvestor);
- }
-// public int nDesiredBTLProperties() {
-// return desiredBTLProperties;
-// }
+ public int findBestRentalQuality(Household me) {
+ int bestQ = 0;
+ double bestU = utilityOfRenting(me,0);
+ double u;
+ for(int q = 0; q bestU) {
+ bestU = u;
+ bestQ = q;
+ }
+ }
+ return(bestQ);
+ }
- /*** @returns expectation value of HPI in one year's time divided by today's HPI*/
- public double HPAExpectation() {
- // Dampening or multiplier factor, depending on its value being <1 or >1, for the current trend of HPA when
- // computing expectations as in HPI(t+DT) = HPI(t) + FACTOR*DT*dHPI/dt (double)
- return(Model.housingMarket.housePriceAppreciation(config.HPA_YEARS_TO_CHECK)*config.HPA_EXPECTATION_FACTOR);
+ public double utilityOfPurchase(Household me, int q, MortgageAgreement mortgage) {
+ double price = Model.housingMarket.getAverageSalePrice(q);
+ if(price > mortgage.purchasePrice) return(-10.0);
+ double principal = price - mortgage.downPayment;
+ double leftForConsumption = 1.0 - (principal*Model.bank.monthlyPaymentFactor(true) - price*HPAExpectation()/12.0)/me.monthlyEmploymentIncome;
+ return(utilityOfHome(me,q) + qualityOfLiving(leftForConsumption));
+
+ }
+
+ public PurchasePlan findBestPurchase(Household me) {
+ PurchasePlan result = new PurchasePlan();
+ MortgageAgreement maxMortgage = Model.bank.requestApproval(me, Model.bank.getMaxMortgage(me, true), downPayment(me) + me.getHomeEquity(), true);
+
+ result.quality = 0;
+ result.utility = utilityOfPurchase(me,0,maxMortgage);
+ double u;
+ for(int q = 1; q result.utility) {
+ result.utility = u;
+ result.quality = q;
+ }
+ }
+ return(result);
+
+ }
+
+ public double utilityOfHome(Household me, int q) {
+ final double rmo = 0.3; // reference housing spend
+ final double k = 0.5; // flexibility of spend on hpi change
+ final double c = k*rmo/(1.0+rmo*(k-1.0)); // 0.0968
+ final double lambda = (rmo-c)/(1-rmo); // 0.290
+ double Pref = (0.05/12.0)*Model.housingMarket.referencePrice(q)/me.monthlyEmploymentIncome;
+ if(Pref < c) return(-10.0);
+ return(lambda*Math.log(Pref-c));
+ }
+
+ public double qualityOfLiving(double consumptionFraction) {
+ return(Math.log(consumptionFraction));
}
-
- /*
- public double utilityOfRenting(Household me, int q) {
- double leftForConsumption = 1.0 - Model.rentalMarket.getAverageSalePrice(q)/me.monthlyEmploymentIncome;
- return(utilityOfHome(me,q) + qualityOfLiving(leftForConsumption));
- }
-
-
- public int findBestRentalQuality(Household me) {
- int bestQ = 0;
- double bestU = utilityOfRenting(me,0);
- double u;
- for(int q = 0; q bestU) {
- bestU = u;
- bestQ = q;
- }
- }
- return(bestQ);
- }
-
- public double utilityOfPurchase(Household me, int q, MortgageAgreement mortgage) {
- double price = Model.housingMarket.getAverageSalePrice(q);
- if(price > mortgage.purchasePrice) return(-10.0);
- double principal = price - mortgage.downPayment;
- double leftForConsumption = 1.0 - (principal*Model.bank.monthlyPaymentFactor(true) - price*HPAExpectation()/12.0)/me.monthlyEmploymentIncome;
- return(utilityOfHome(me,q) + qualityOfLiving(leftForConsumption));
-
- }
-
- public PurchasePlan findBestPurchase(Household me) {
- PurchasePlan result = new PurchasePlan();
- MortgageAgreement maxMortgage = Model.bank.requestApproval(me, Model.bank.getMaxMortgage(me, true), downPayment(me) + me.getHomeEquity(), true);
-
- result.quality = 0;
- result.utility = utilityOfPurchase(me,0,maxMortgage);
- double u;
- for(int q = 1; q result.utility) {
- result.utility = u;
- result.quality = q;
- }
- }
- return(result);
-
- }
-
- public double utilityOfHome(Household me, int q) {
- final double rmo = 0.3; // reference housing spend
- final double k = 0.5; // flexibility of spend on hpi change
- final double c = k*rmo/(1.0+rmo*(k-1.0)); // 0.0968
- final double lambda = (rmo-c)/(1-rmo); // 0.290
- double Pref = (0.05/12.0)*Model.housingMarket.referencePrice(q)/me.monthlyEmploymentIncome;
- if(Pref < c) return(-10.0);
- return(lambda*Math.log(Pref-c));
- }
-
- public double qualityOfLiving(double consumptionFraction) {
- return(Math.log(consumptionFraction));
- }
- */
+ */
}
diff --git a/src/main/java/housing/Lifecycle.java b/src/main/java/housing/Lifecycle.java
index 3e49d2e5..146517bc 100644
--- a/src/main/java/housing/Lifecycle.java
+++ b/src/main/java/housing/Lifecycle.java
@@ -7,8 +7,6 @@
public class Lifecycle implements Serializable {
private static final long serialVersionUID = -2455155016204679970L;
- private Config config = Model.config; // Passes the Model's configuration parameters object to a private field
-
public Lifecycle(double iage) {
age = iage;
incomePercentile = rand.nextDouble();
diff --git a/src/main/java/housing/Model.java b/src/main/java/housing/Model.java
index 426d4925..ed4599f4 100644
--- a/src/main/java/housing/Model.java
+++ b/src/main/java/housing/Model.java
@@ -11,6 +11,7 @@
import collectors.HousingMarketStats;
import collectors.MicroDataRecorder;
import collectors.Recorder;
+import configuration.ModelConfig;
import org.apache.commons.math3.random.RandomGenerator;
import ec.util.MersenneTwisterFast;
@@ -28,298 +29,299 @@
@SuppressWarnings("serial")
public class Model extends SimState implements Steppable {
- ////////////////////////////////////////////////////////////////////////
-
- /*
- * ATTENTION: Seed for random number generation is set by calling the program with argument "-seed ",
- * where must be a positive integer. In the absence of this argument, seed is set from machine time.
- */
-
- public static Config config;
-
- ////////////////////////////////////////////////////////////////////////
-
- public static void main(String[] args) {
- //doLoop(ModelNoGUI.class, args);
- doLoop(Model.class,args);
- System.exit(0); //Stop the program when finished.
- }
-
- public Model(long seed) {
- super(seed);
- rand = new MersenneTwister(seed);
- config = new Config("src/main/resources/config.properties");
- //System.exit(0);
-
- government = new Government();
- demographics = new Demographics();
- recorder = new collectors.Recorder();
- transactionRecorder = new collectors.MicroDataRecorder();
-
- centralBank = new CentralBank();
- mBank = new Bank();
- mConstruction = new Construction();
- mHouseholds = new ArrayList(config.TARGET_POPULATION*2);
- housingMarket = mHousingMarket = new HouseSaleMarket(); // Variables of housingMarket are initialised (including HPI)
- rentalMarket = mRentalMarket = new HouseRentalMarket(); // Variables of rentalMarket are initialised (including HPI)
- mCollectors = new collectors.Collectors();
- nSimulation = 0;
-
- setupStatics();
- init(); // Variables of both housingMarket and rentalMarket are initialised again (including HPI)
- }
-
- @Override
- public void awakeFromCheckpoint() {
- super.awakeFromCheckpoint();
- setupStatics();
- }
-
- protected void setupStatics() {
-// centralBank = mCentralBank;
- bank = mBank;
- construction = mConstruction;
- households = mHouseholds;
- housingMarket = mHousingMarket;
- rentalMarket = mRentalMarket;
- collectors = mCollectors;
- root = this;
- setRecordCoreIndicators(config.recordCoreIndicators);
- setRecordMicroData(config.recordMicroData);
- }
-
- public void init() {
- construction.init();
- housingMarket.init();
- rentalMarket.init();
- bank.init();
- households.clear();
- collectors.init();
- t = 0;
- if(!monteCarloCheckpoint.equals("")) { //changed this from != ""
- File f = new File(monteCarloCheckpoint);
- readFromCheckpoint(f);
- }
- }
-
- /**
- * This method is called before the simulation starts. It schedules this
- * object to be stepped at each timestep and initialises the agents.
- */
- public void start() {
- super.start();
- scheduleRepeat = schedule.scheduleRepeating(this);
-
- if(!monteCarloCheckpoint.equals("")) { //changed from != ""
- File f = new File(monteCarloCheckpoint);
- readFromCheckpoint(f);
- }
-// recorder.start();
- }
-
- public void stop() {
- scheduleRepeat.stop();
- }
-
- /**
- * This is the main time-step of the whole simulation. Everything starts
- * here.
- */
- public void step(SimState simulationStateNow) {
- if (schedule.getTime() >= config.N_STEPS*config.N_SIMS) simulationStateNow.kill();
- if(t >= config.N_STEPS) {
- // start new simulation
- nSimulation += 1;
- if (nSimulation >= config.N_SIMS) {
- // this was the last simulation, clean up
- if(config.recordCoreIndicators) recorder.finish();
- if(config.recordMicroData) transactionRecorder.finish();
- simulationStateNow.kill();
- return;
- }
- if(config.recordCoreIndicators) recorder.endOfSim();
- if(config.recordMicroData) transactionRecorder.endOfSim();
- init(); // Variables of both housingMarket and rentalMarket are initialised again (including HPI)
- }
-
- /*
- * Steps model and stores ownership and rental markets bid and offer prices, and their averages, into their
- * respective variables
- */
- modelStep();
-
- if (t>=config.TIME_TO_START_RECORDING) {
- // Finds values of variables and records them to their respective files
- if(config.recordCoreIndicators) recorder.step();
- }
-
- collectors.step();
- }
-
- public void modelStep() {
- demographics.step();
- construction.step();
-
- for(Household h : households) h.step();
+ ////////////////////////////////////////////////////////////////////////
+
+ /*
+ * ATTENTION: Seed for random number generation is set by calling the program with argument "-seed ",
+ * where must be a positive integer. In the absence of this argument, seed is set from machine time.
+ */
+
+ public static ModelConfig config;
+
+ ////////////////////////////////////////////////////////////////////////
+
+ public static void main(String[] args) {
+ //doLoop(ModelNoGUI.class, args);
+ doLoop(Model.class,args);
+ System.exit(0); //Stop the program when finished.
+ }
+
+ public Model(long seed) {
+ super(seed);
+ rand = new MersenneTwister(seed);
+
+ config = new ModelConfig("model.conf");
+
+
+ government = new Government(config.getGovernmentConfig());
+ demographics = new Demographics();
+ recorder = new collectors.Recorder();
+ transactionRecorder = new collectors.MicroDataRecorder();
+
+ centralBank = new CentralBank(config.getCentralBankConfig());
+ mBank = new Bank();
+ mConstruction = new Construction();
+ mHouseholds = new ArrayList(config.TARGET_POPULATION*2);
+ housingMarket = mHousingMarket = new HouseSaleMarket(); // Variables of housingMarket are initialised (including HPI)
+ rentalMarket = mRentalMarket = new HouseRentalMarket(); // Variables of rentalMarket are initialised (including HPI)
+ mCollectors = new collectors.Collectors();
+ nSimulation = 0;
+
+ setupStatics();
+ init(); // Variables of both housingMarket and rentalMarket are initialised again (including HPI)
+ }
+
+ @Override
+ public void awakeFromCheckpoint() {
+ super.awakeFromCheckpoint();
+ setupStatics();
+ }
+
+ protected void setupStatics() {
+// centralBank = mCentralBank;
+ bank = mBank;
+ construction = mConstruction;
+ households = mHouseholds;
+ housingMarket = mHousingMarket;
+ rentalMarket = mRentalMarket;
+ collectors = mCollectors;
+ root = this;
+ setRecordCoreIndicators(config.recordCoreIndicators);
+ setRecordMicroData(config.recordMicroData);
+ }
+
+ public void init() {
+ construction.init();
+ housingMarket.init();
+ rentalMarket.init();
+ bank.init();
+ households.clear();
+ collectors.init();
+ t = 0;
+ if(!monteCarloCheckpoint.equals("")) { //changed this from != ""
+ File f = new File(monteCarloCheckpoint);
+ readFromCheckpoint(f);
+ }
+ }
+
+ /**
+ * This method is called before the simulation starts. It schedules this
+ * object to be stepped at each timestep and initialises the agents.
+ */
+ public void start() {
+ super.start();
+ scheduleRepeat = schedule.scheduleRepeating(this);
+
+ if(!monteCarloCheckpoint.equals("")) { //changed from != ""
+ File f = new File(monteCarloCheckpoint);
+ readFromCheckpoint(f);
+ }
+// recorder.start();
+ }
+
+ public void stop() {
+ scheduleRepeat.stop();
+ }
+
+ /**
+ * This is the main time-step of the whole simulation. Everything starts
+ * here.
+ */
+ public void step(SimState simulationStateNow) {
+ if (schedule.getTime() >= config.N_STEPS*config.N_SIMS) simulationStateNow.kill();
+ if(t >= config.N_STEPS) {
+ // start new simulation
+ nSimulation += 1;
+ if (nSimulation >= config.N_SIMS) {
+ // this was the last simulation, clean up
+ if(config.recordCoreIndicators) recorder.finish();
+ if(config.recordMicroData) transactionRecorder.finish();
+ simulationStateNow.kill();
+ return;
+ }
+ if(config.recordCoreIndicators) recorder.endOfSim();
+ if(config.recordMicroData) transactionRecorder.endOfSim();
+ init(); // Variables of both housingMarket and rentalMarket are initialised again (including HPI)
+ }
+
+ /*
+ * Steps model and stores ownership and rental markets bid and offer prices, and their averages, into their
+ * respective variables
+ */
+ modelStep();
+
+ if (t>=config.TIME_TO_START_RECORDING) {
+ // Finds values of variables and records them to their respective files
+ if(config.recordCoreIndicators) recorder.step();
+ }
+
+ collectors.step();
+ }
+
+ public void modelStep() {
+ demographics.step();
+ construction.step();
+
+ for(Household h : households) h.step();
// Stores ownership market bid and offer prices, and their averages, into their respective variables
- collectors.housingMarketStats.record();
+ collectors.housingMarketStats.record();
// Clears market and updates the HPI
- housingMarket.clearMarket();
+ housingMarket.clearMarket();
// Stores rental market bid and offer prices, and their averages, into their respective variables
- collectors.rentalMarketStats.record();
- rentalMarket.clearMarket();
- bank.step();
- centralBank.step(getCoreIndicators());
- t += 1;
- }
-
-
- /**
- * Cleans up after a simulation ends.
- */
- public void finish() {
- super.finish();
- if(config.recordCoreIndicators) recorder.finish();
- if(config.recordMicroData) transactionRecorder.finish();
- }
-
- /**
- * @return simulated time in months
- */
- static public int getTime() {
- return(Model.root.t);
- }
-
- static public int getMonth() {
- return(Model.root.t%12 + 1);
- }
-
- public Stoppable scheduleRepeat;
-
- // non-statics for serialization
- public ArrayList mHouseholds;
- public Bank mBank;
-// public CentralBank mCentralBank;
- public Construction mConstruction;
- public HouseSaleMarket mHousingMarket;
- public HouseRentalMarket mRentalMarket;
- public collectors.Collectors mCollectors;
-
- public static CentralBank centralBank;
- public static Bank bank;
- public static Government government;
- public static Construction construction;
- public static HouseSaleMarket housingMarket;
- public static HouseRentalMarket rentalMarket;
- public static ArrayList households;
- public static Demographics demographics;
- public static MersenneTwister rand;
- public static Model root;
-
- public static collectors.Collectors collectors; // = new Collectors();
- public static Recorder recorder; // records info to file
- public static MicroDataRecorder transactionRecorder;
-
- public static int nSimulation; // number of simulations run
- public int t; // time (months)
-// public static LogNormalDistribution grossFinancialWealth; // household wealth in bank balances and investments
-
- /**
- * proxy class to allow us to work with apache.commons distributions
- */
- public static class MersenneTwister extends MersenneTwisterFast implements RandomGenerator {
- public MersenneTwister(long seed) {super(seed);}
- public void setSeed(int arg0) {
- super.setSeed((long)arg0);
- }
- }
-
- ////////////////////////////////////////////////////////////////////////
- // Getters/setters for MASON console
- ////////////////////////////////////////////////////////////////////////
-
- public CreditSupply getCreditSupply() {
- return collectors.creditSupply;
- }
-
- public collectors.HousingMarketStats getHousingMarketStats() {
- return collectors.housingMarketStats;
- }
-
- public HousingMarketStats getRentalMarketStats() {
- return collectors.rentalMarketStats;
- }
-
- public CoreIndicators getCoreIndicators() {
- return collectors.coreIndicators;
- }
-
- public HouseholdStats getHouseholdStats() {
- return collectors.householdStats;
- }
-
- String monteCarloCheckpoint = "";
-
- public String getMonteCarloCheckpoint() {
- return monteCarloCheckpoint;
- }
-
- public void setMonteCarloCheckpoint(String monteCarloCheckpoint) {
- this.monteCarloCheckpoint = monteCarloCheckpoint;
- }
-
-// Deprecated getters/setters! New access to parameters is through instances of the Config class
-// public static int getN_STEPS() {
-// return N_STEPS;
-// }
+ collectors.rentalMarketStats.record();
+ rentalMarket.clearMarket();
+ bank.step();
+ centralBank.step(getCoreIndicators());
+ t += 1;
+ }
+
+
+ /**
+ * Cleans up after a simulation ends.
+ */
+ public void finish() {
+ super.finish();
+ if(config.recordCoreIndicators) recorder.finish();
+ if(config.recordMicroData) transactionRecorder.finish();
+ }
+
+ /**
+ * @return simulated time in months
+ */
+ static public int getTime() {
+ return(Model.root.t);
+ }
+
+ static public int getMonth() {
+ return(Model.root.t%12 + 1);
+ }
+
+ public Stoppable scheduleRepeat;
+
+ // non-statics for serialization
+ public ArrayList mHouseholds;
+ public Bank mBank;
+// public CentralBank mCentralBank;
+ public Construction mConstruction;
+ public HouseSaleMarket mHousingMarket;
+ public HouseRentalMarket mRentalMarket;
+ public collectors.Collectors mCollectors;
+
+ public static CentralBank centralBank;
+ public static Bank bank;
+ public static Government government;
+ public static Construction construction;
+ public static HouseSaleMarket housingMarket;
+ public static HouseRentalMarket rentalMarket;
+ public static ArrayList households;
+ public static Demographics demographics;
+ public static MersenneTwister rand;
+ public static Model root;
+
+ public static collectors.Collectors collectors; // = new Collectors();
+ public static Recorder recorder; // records info to file
+ public static MicroDataRecorder transactionRecorder;
+
+ public static int nSimulation; // number of simulations run
+ public int t; // time (months)
+// public static LogNormalDistribution grossFinancialWealth; // household wealth in bank balances and investments
+
+ /**
+ * proxy class to allow us to work with apache.commons distributions
+ */
+ public static class MersenneTwister extends MersenneTwisterFast implements RandomGenerator {
+ public MersenneTwister(long seed) {super(seed);}
+ public void setSeed(int arg0) {
+ super.setSeed((long)arg0);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Getters/setters for MASON console
+ ////////////////////////////////////////////////////////////////////////
+
+ public CreditSupply getCreditSupply() {
+ return collectors.creditSupply;
+ }
+
+ public collectors.HousingMarketStats getHousingMarketStats() {
+ return collectors.housingMarketStats;
+ }
+
+ public HousingMarketStats getRentalMarketStats() {
+ return collectors.rentalMarketStats;
+ }
+
+ public CoreIndicators getCoreIndicators() {
+ return collectors.coreIndicators;
+ }
+
+ public HouseholdStats getHouseholdStats() {
+ return collectors.householdStats;
+ }
+
+ String monteCarloCheckpoint = "";
+
+ public String getMonteCarloCheckpoint() {
+ return monteCarloCheckpoint;
+ }
+
+ public void setMonteCarloCheckpoint(String monteCarloCheckpoint) {
+ this.monteCarloCheckpoint = monteCarloCheckpoint;
+ }
+
+// Deprecated getters/setters! New access to parameters is through instances of the Config class
+// public static int getN_STEPS() {
+// return N_STEPS;
+// }
//
-// public static void setN_STEPS(int n_STEPS) {
-// N_STEPS = n_STEPS;
-// }
-// public String nameN_STEPS() {return("Number of timesteps");}
+// public static void setN_STEPS(int n_STEPS) {
+// N_STEPS = n_STEPS;
+// }
+// public String nameN_STEPS() {return("Number of timesteps");}
//
-// public static int getN_SIMS() {
-// return N_SIMS;
-// }
+// public static int getN_SIMS() {
+// return N_SIMS;
+// }
//
-// public static void setN_SIMS(int n_SIMS) {
-// N_SIMS = n_SIMS;
-// }
-// public String nameN_SIMS() {return("Number of monte-carlo runs");}
+// public static void setN_SIMS(int n_SIMS) {
+// N_SIMS = n_SIMS;
+// }
+// public String nameN_SIMS() {return("Number of monte-carlo runs");}
//
-// public boolean isRecordCoreIndicators() {
-// return recordCoreIndicators;
-// }
-
- public void setRecordCoreIndicators(boolean recordCoreIndicators) {
- this.config.recordCoreIndicators = recordCoreIndicators;
- if(recordCoreIndicators) {
- collectors.coreIndicators.setActive(true);
- collectors.creditSupply.setActive(true);
- collectors.householdStats.setActive(true);
- collectors.housingMarketStats.setActive(true);
- collectors.rentalMarketStats.setActive(true);
- try {
- recorder.start();
- } catch (FileNotFoundException | UnsupportedEncodingException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-// else {
-// recorder.finish();
-// }
- }
- public String nameRecordCoreIndicators() {return("Record core indicators");}
-
- public boolean isRecordMicroData() {
- return transactionRecorder.isActive();
- }
-
- public void setRecordMicroData(boolean record) {
- transactionRecorder.setActive(record);
- }
- public String nameRecordMicroData() {return("Record micro data");}
+// public boolean isRecordCoreIndicators() {
+// return recordCoreIndicators;
+// }
+
+ public void setRecordCoreIndicators(boolean recordCoreIndicators) {
+ this.config.recordCoreIndicators = recordCoreIndicators;
+ if(recordCoreIndicators) {
+ collectors.coreIndicators.setActive(true);
+ collectors.creditSupply.setActive(true);
+ collectors.householdStats.setActive(true);
+ collectors.housingMarketStats.setActive(true);
+ collectors.rentalMarketStats.setActive(true);
+ try {
+ recorder.start();
+ } catch (FileNotFoundException | UnsupportedEncodingException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+// else {
+// recorder.finish();
+// }
+ }
+ public String nameRecordCoreIndicators() {return("Record core indicators");}
+
+ public boolean isRecordMicroData() {
+ return transactionRecorder.isActive();
+ }
+
+ public void setRecordMicroData(boolean record) {
+ transactionRecorder.setActive(record);
+ }
+ public String nameRecordMicroData() {return("Record micro data");}
}
diff --git a/src/main/resources/model.conf b/src/main/resources/model.conf
new file mode 100644
index 00000000..0d38b9f5
--- /dev/null
+++ b/src/main/resources/model.conf
@@ -0,0 +1,356 @@
+simulation {
+ ##################################################
+ ################ General comments ################
+ ##################################################
+
+ # Seed for random number generation is set by calling the program with argument
+ # "-seed ", where must be a positive integer. In the
+ # absence of this argument, seed is set from machine time
+ # todo seed should be specified here in the configuration file!
+ ##################################################
+ ######## General model control parameters ########
+ ##################################################
+
+ # Simulation duration in time steps (int)
+ N_STEPS = 10000
+ # Time steps before recording statistics, initialisation time (int)
+ TIME_TO_START_RECORDING = 0
+ # Number of simulations to run (int)
+ N_SIMS = 1
+ # True to write time series for each core indicator (boolean)
+ recordCoreIndicators = true
+ # True to write micro data for each transaction made (boolean)
+ recordMicroData = false
+
+ ##################################################
+ ################ House parameters ################
+ ##################################################
+
+ # Number of quality bands for houses (int)
+ N_QUALITY = 48
+
+ ##################################################
+ ########### Housing market parameters ############
+ ##################################################
+ market {
+
+ # Time, in days, that a house remains under offer (int)
+ days-under-offer = 7
+
+ # Smallest proportional increase in price that can cause a gazump (double)
+ bid-up = 1.0075
+
+ # Decay constant for the exponential moving average of sale prices (double)
+ market-average-price-decay = 0.25
+
+ # Initial housing price index, HPI (double)
+ # TODO: Reference for this value and justification for this discounting are needed! (the parameter is declared, but no source nor justification is given)
+ initial-hpi = 0.8
+
+ # Median house price (double)
+ # TODO: Replace by the 2011 value
+ hpi-median = 195000.0
+
+ # Shape parameter for the log-normal distribution of housing prices, taken from the ONS (2013) house price index data tables, table 34 (double)
+ # TODO: Replace by the 2011 value, compare with Land Registry Paid Price data and decide whether to use real distribution
+ hpi-shape = 0.555
+
+ # Average number of months a tenant will stay in a rented house. Source: ARLA - Members survey of the Private Rented Sector Q4 2013
+ # TODO: Replace with 2011 value
+ average-tenacy-length = 18
+
+ # Profit margin for buy-to-let investors (double)
+ # Yield on rent had average 6% between 2009/01 and 2015/01, minimum in 2009/10 maximum in 2012/04 peak-to-peak amplitude of 0.4%. Source: Bank of England, unpublished analysis based on Zoopla/Land Registry matching (Philippe Bracke)
+ rent-gross-yield = 0.05
+
+ }
+
+ ##################################################
+ ############# Demographic parameters #############
+ ##################################################
+ demographics {
+ # Target number of households (int)
+ target-population = 10000
+ # TODO: Unclear parameter related to the creation of the population (boolean)
+ spin-up = false
+ # Future birth rate (births per year per capita), calibrated with flux of FTBs, Council of Mortgage Lenders Regulated Mortgage Survey, 2015 (double)
+ future-birth-rate = 0.018
+ }
+ ##################################################
+ ############## Household parameters ##############
+ ##################################################
+ households {
+ # True to have a buy-to-let sector (boolean)
+ BTL_ENABLED = true
+ # Monthly percentage growth of financial investments (double)
+ RETURN_ON_FINANCIAL_WEALTH = 0.002
+ # Average number of months a tenant will stay in a rented house (int)
+ # Source: ARLA - Members survey of the Private Rented Sector Q4 2013
+ TENANCY_LENGTH_AVERAGE = 18
+ # Standard deviation of the noise in determining the tenancy length (int)
+ TENANCY_LENGTH_EPSILON = 6
+
+ ##################################################
+ ######### Household behaviour parameters #########
+ ##################################################
+ behavior {
+
+ ############# Buy-To-Let parameters ##############
+ buy-to-let {
+
+ # Prior probability of being (wanting to be) a BTL investor (double)
+ # TODO: Shouldn't this be 4% according to the article?
+ probability-investor = 0.16
+
+ # Minimum income percentile for a household to be a BTL investor (double)
+ min-income-percentile = 0.5
+
+ # Weight that fundamentalists put on cap gain (double)
+ fundamentalist-capital-gain-coefficient = 0.5
+
+ # Weight that trend-followers put on cap gain (double)
+ trend-followers-capital-gain-coefficient = 0.9
+
+ # Probability that a BTL investor is a fundamentalist versus a trend-follower (double)
+ probability-fundamentalist = 0.5
+
+ # Chooses between two possible equations for BTL investors to make their buy/sell decisions (boolean)
+ buy-to-let-yield-scaling = false
+
+ }
+
+ ################ Rent parameters #################
+ # Desired proportion of income to be spent on rent (double)
+ DESIRED_RENT_INCOME_FRACTION = 0.33
+ # Annual psychological cost of renting (double)
+ # TODO: This value comes from 1.1/12.0... Where does that come from?
+ PSYCHOLOGICAL_COST_OF_RENTING = 0.0916666666667
+ # Sensitivity parameter of the decision between buying and renting (double)
+ # TODO: This value comes from 1.0/3500.0... Where does that come from?
+ SENSITIVITY_RENT_OR_PURCHASE = 0.000285714285714
+
+ ############### General parameters ###############
+ # If the ratio between the buyer's bank balance and the house price is above this,
+ # payment will be made fully in cash (double)
+ BANK_BALANCE_FOR_CASH_DOWNPAYMENT = 2.0
+ # Dampening or multiplier factor, depending on its value being <1 or >1, for the current trend when computing expectations as in
+ # HPI(t+DT) = HPI(t) + FACTOR*DT*dHPI/dt (double)
+ # TODO: According to John Muellbauer, this is a dampening factor (<1). Find a reference for this!
+ HPA_EXPECTATION_FACTOR = 0.5
+ # Number of years of the HPI record to check when computing the annual HPA, i.e., how much backward looking households are (int)
+ HPA_YEARS_TO_CHECK = 1
+ # Average period, in years, for which owner-occupiers hold their houses (double)
+ # British housing survey 2008
+ HOLD_PERIOD = 11.0
+
+ ######### Sale price reduction parameters ########
+ # This subsection was calibrated against Zoopla data at the BoE
+ # Monthly probability of reducing the price of a house on the market (double)
+ # This value comes from 1.0-0.945
+ P_SALE_PRICE_REDUCE = 0.055
+ # Mean percentage reduction for prices of houses on the market (double)
+ REDUCTION_MU = 1.603
+ # Standard deviation of percentage reductions for prices of houses on the market (double)
+ REDUCTION_SIGMA = 0.617
+
+ ############# Comsumption parameters #############
+ consumption-decision-rule {
+
+ # Fraction of the monthly budget allocated for consumption, being the monthly
+ # budget equal to the bank balance minus the minimum desired bank balance (double)
+ consumption-fraction = 0.5
+
+ # Fraction of Government support representing the amount necessarily spent monthly by
+ # all households as essential consumption (double)
+ essential-consumption-fraction = 0.8
+
+ }
+ ######### Initial sale price parameters ##########
+ # Initial markup over average price of same quality houses (double)
+ # TODO: Note says that, according to BoE calibration, this should be around 0.2. Check and solve this!
+ SALE_MARKUP = 0.04
+ # Weight of the days-on-market effect (double)
+ SALE_WEIGHT_DAYS_ON_MARKET = 0.011
+ # Standard deviation of the noise (double)
+ SALE_EPSILON = 0.05
+
+ ##### Buyer's desired expenditure parameters #####
+ # Scale, number of annual salaries the buyer is willing to spend for buying a house (double)
+ # TODO: This has been macro-calibrated against owner-occupier LTI and LTV ration, core indicators average 1987-2006. Find sources!
+ BUY_SCALE = 4.5
+ # Weight given to house price appreciation when deciding how much to spend for buying a house (double)
+ BUY_WEIGHT_HPA = 0.08
+ # Standard deviation of the noise (double)
+ BUY_EPSILON = 0.14
+
+ ############ Demanded rent parameters ############
+ # Markup over average rent demanded for houses of the same quality (double)
+ RENT_MARKUP = 0.00
+ # Number of months on the market in an equilibrium situation (double)
+ RENT_EQ_MONTHS_ON_MARKET = 6.0
+ # Standard deviation of the noise (double)
+ RENT_EPSILON = 0.05
+ # Maximum period of time BTL investors are ready to wait to get back their investment through rents,
+ # this determines the minimum rent they are ready to accept (double)
+ # TODO: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Attention: This parameter and its associated mechanism are not declared in the article! Need to declare and reference!
+ RENT_MAX_AMORTIZATION_PERIOD = 20.833333333
+ # Percentage reduction of demanded rent for every month the property is in the market, not rented (double)
+ RENT_REDUCTION = 0.05
+
+ ############# Downpayment parameters #############
+ # Minimum income percentile to consider any downpayment, below this level, downpayment is set to 0 (double)
+ # TODO: Calibrated against PSD data, need clearer reference or disclose distribution!
+ downpayment {
+
+ DOWNPAYMENT_MIN_INCOME = 0.3
+ # TODO: Both functional form and parameters are micro-calibrated against BoE data. Need reference or disclose distribution!
+ # Scale parameter for the log-normal distribution of downpayments by first-time-buyers (double)
+ DOWNPAYMENT_FTB_SCALE = 10.30
+ # Shape parameter for the log-normal distribution of downpayments by first-time-buyers (double)
+ DOWNPAYMENT_FTB_SHAPE = 0.9093
+ # Scale parameter for the log-normal distribution of downpayments by owner-occupiers (double)
+ DOWNPAYMENT_OO_SCALE = 11.155
+ # Shape parameter for the log-normal distribution of downpayments by owner-occupiers (double)
+ DOWNPAYMENT_OO_SHAPE = 0.7538
+ # Average downpayment, as percentage of house price, by but-to-let investors (double)
+ # TODO: Said to be calibrated to match LTV ratios, but no reference is given. Need reference!
+ # TODO: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Attention: Functional form slightly different to the one presented in the article
+ DOWNPAYMENT_BTL_MEAN = 0.3
+ # Standard deviation of the noise (double)
+ DOWNPAYMENT_BTL_EPSILON = 0.1
+ }
+ ######## Desired bank balance parameters #########
+ # Micro-calibrated to match the log-normal relationship between wealth and income from the Wealth and Assets Survey
+ desired-bank-balance {
+
+ alpha = -32.0013877
+
+ beta = 4.07
+
+ epsilon = 0.1
+
+ }
+
+ ########## Selling decision parameters ###########
+ # Weight of houses per capita effect
+ selling {
+
+ DECISION_TO_SELL_ALPHA = 4.0
+ # Weight of interest rate effect
+ DECISION_TO_SELL_BETA = 5.0
+ # TODO: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Attention: fudge parameter, explicitly explained otherwise in the article
+ DECISION_TO_SELL_HPC = 0.05
+ # TODO: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Attention: fudge parameter, explicitly explained otherwise in the article
+ DECISION_TO_SELL_INTEREST = 0.03
+
+ ######### BTL buy/sell choice parameters #########
+ # Shape parameter, or intensity of choice on effective yield (double)
+ BTL_CHOICE_INTENSITY = 50.0
+ # Minimun bank balance, as a percentage of the desired bank balance, to buy new properties (double)
+ # TODO: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ Attention: This parameter and its associated mechanism are not declared in the article! Need to declare and reference!
+ BTL_CHOICE_MIN_BANK_BALANCE = 0.75
+
+ }
+ }
+
+ ##################################################
+ ################ Bank parameters #################
+ ##################################################
+ # TODO: We need references or justification for all these values!
+ bank {
+
+ # Mortgage duration in years (int)
+ mortgage-duration-years = 25
+
+ # Bank initial base-rate, which remains currently unchanged (double)
+ initial-base-rate = 0.005
+
+ # Bank's target supply of credit per household per month (double)
+ credit-supply-target = 380
+
+ }
+
+ ##################################################
+ ############# Central bank parameters ############
+ ##################################################
+ # TODO: We need references or justification for all these values! Also, need to clarify meaning of "when not regulated"
+ central-bank {
+
+ # Maximum LTV ratio that the bank would allow for first-time-buyers when not regulated (double)
+ max-first-time-buyer-ltv = 0.95
+
+ # Maximum LTV ratio that the bank would allow for owner-occupiers when not regulated (double)
+ max-owner-occupiers-ltv = 0.9
+
+ # Maximum LTV ratio that the bank would allow for BTL investors when not regulated (double)
+ max-buy-to-let-ltv = 0.8
+
+ # Maximum fraction of mortgages that the bank can give over the LTV ratio limit (double)
+ fraction-over-max-ltv = 0.0
+
+ # Maximum LTI ratio that the bank would allow for first-time-buyers when not regulated (double)
+ max-first-time-buyers-lti = 6.0
+
+ # Maximum LTI ratio that the bank would allow for owner-occupiers when not regulated (double)
+ max-owner-occupiers-lti = 6.0
+
+ # Maximum fraction of mortgages that the bank can give over the LTI ratio limit (double)
+ fraction-over-max-lti = 0.15
+
+ # Maximum fraction of the household's income to be spent on mortgage repayments under stressed conditions (double)
+ affordability-coeff = 0.5
+
+ # Interest rate under stressed condition for BTL investors when calculating interest coverage ratios, ICR (double)
+ buy-to-let-stressed-interest = 0.05
+
+ # Interest coverage ratio (ICR) limit imposed by the central bank (double)
+ max-interest-coverage-ratio = 1.25
+
+ }
+
+ ##################################################
+ ############ Construction parameters #############
+ ##################################################
+ # TODO: We need references or justification for all these values!
+
+ # Target ratio of houses per household (double)
+ CONSTRUCTION_HOUSES_PER_HOUSEHOLD = 0.82
+
+ ##################################################
+ ############# Government parameters ##############
+ ##################################################
+ # TODO: We need references or justification for all these values!
+ government {
+ # Maximum personal allowance (double)
+ personal-allowance-limit = 100000.0
+ # Minimum monthly earnings for a married couple from income support (double)
+ income-support = 492.7
+ }
+ ##################################################
+ ############## Collectors parameters #############
+ ##################################################
+
+ # Approximate number of households in UK, used to scale up results for core indicators (double)
+ # TODO: Reference needed
+ UK_HOUSEHOLDS = 26.5e6
+ # Whether to record mortgage statistics (boolean)
+ MORTGAGE_DIAGNOSTICS_ACTIVE = true
+
+ ##################################################
+ ################# Data addresses #################
+ ##################################################
+
+ ############ Government data addresses ###########
+ # TODO: We need clearer references for the values contained in these files! Also, current values are for 2013/2014, replace for 2011!
+ DATA_TAX_RATES = "src/main/resources/TaxRates.csv"
+ DATA_NATIONAL_INSURANCE_RATES = "src/main/resources/NationalInsuranceRates.csv"
+
+ ############ Lifecycle data addresses ############
+ DATA_INCOME_GIVEN_AGE = "src/main/resources/IncomeGivenAge.csv"
+
+ ########### Demographics data addresses ##########
+ # Target probability density of age of representative person in the household at time t=0, calibrated against LCFS (2012)
+ DATA_AGE_MARGINAL_PDF = "src/main/resources/AgeMarginalPDFstatic.csv"
+ DATA_HOUSEHOLD_AGE_AT_BIRTH_PDF = "src/main/resources/HouseholdAgeAtBirthPDF.csv"
+ DATA_DEATH_PROB_GIVEN_AGE = "src/main/resources/DeathProbGivenAge.csv"
+}
\ No newline at end of file