Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tidy up of the SocialRent class #28

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions app/models/Household.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { FairholdLandPurchase } from "./tenure/FairholdLandPurchase";
import { FairholdLandRent } from "./tenure/FairholdLandRent";
import { Fairhold } from "./Fairhold";
import { Property } from "./Property";
import { SocialRent } from "./tenure/SocialRent";
import { SocialRent, socialRentAdjustmentTypes } from "./tenure/SocialRent";
import { ForecastParameters } from "./ForecastParameters";

export class Household {
Expand Down Expand Up @@ -39,7 +39,7 @@ export class Household {
incomePerPersonYearly: number;
averageRentYearly: number;
socialRentAverageEarning: number;
socialRentAdjustments: any;
socialRentAdjustments: socialRentAdjustmentTypes;
housePriceIndex: number;
gasBillYearly: number;
property: Property;
Expand Down Expand Up @@ -72,7 +72,7 @@ export class Household {
calculateTenures(
averageRentYearly: number,
socialRentAverageEarning: number,
socialRentAdjustments: any,
socialRentAdjustments: socialRentAdjustmentTypes,
housePriceIndex: number
) {
if (this.incomeYearly == undefined) throw new Error("income is undefined");
Expand Down Expand Up @@ -126,7 +126,8 @@ export class Household {
socialRentAverageEarning: socialRentAverageEarning,
socialRentAdjustments: socialRentAdjustments,
housePriceIndex: housePriceIndex,
property: this.property,
landToTotalRatio: this.property.landToTotalRatio,
numberOfBedrooms: this.property.numberOfBedrooms,
});

if (this.tenure.marketPurchase.affordability == undefined)
Expand Down
25 changes: 14 additions & 11 deletions app/models/Property.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@

import * as math from "mathjs";

import { BED_WEIGHTS_AND_CAPS } from "./constants";
/**
* Number of decimal places to use when rounding numerical values
*/
const PRECISION = 2
const PRECISION = 2;
const DEPRECIATION_FACTOR = -32938;

type PropertyParams = Pick<
Expand Down Expand Up @@ -90,18 +89,22 @@ export class Property {
return depreciatedBuildPrice;
}

private calculateBedWeightedAveragePrice(
beds: number[] = [0, 1, 2, 3, 4, 5, 6],
bedWeights: number[] = [0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4],
) {
private calculateBedWeightedAveragePrice() {
const beds = BED_WEIGHTS_AND_CAPS.numberOfBedrooms;
const bedWeights = BED_WEIGHTS_AND_CAPS.weight;
let bedWeight;

if (this.numberOfBedrooms < beds[beds.length - 1]) {
if (
this.numberOfBedrooms <
BED_WEIGHTS_AND_CAPS.numberOfBedrooms[
BED_WEIGHTS_AND_CAPS.numberOfBedrooms.length - 1
]
) {
// assign the weight based on the number of beds
bedWeight = bedWeights[this.numberOfBedrooms];
bedWeight = BED_WEIGHTS_AND_CAPS.weight[this.numberOfBedrooms];
} else {
// assign the last value if out of scale
bedWeight = bedWeights[bedWeights.length - 1];
bedWeight = BED_WEIGHTS_AND_CAPS.weight[bedWeights.length - 1];
}

bedWeight = parseFloat(
Expand All @@ -110,4 +113,4 @@ export class Property {

return bedWeight;
}
}
}
33 changes: 32 additions & 1 deletion app/models/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1,32 @@
export const MONTHS_PER_YEAR = 12;
export const MONTHS_PER_YEAR = 12;
export const WEEKS_PER_MONTH = 4.2;

export type bedWeightsAndCapsType = {
numberOfBedrooms: number[];
weight: number[];
socialRentCap: number[];
};

/**
* multiplying value weight and social rent cap for a property based on number of bed rooms
*/
export const BED_WEIGHTS_AND_CAPS: bedWeightsAndCapsType = {
numberOfBedrooms: [0, 1, 2, 3, 4, 5, 6],
weight: [0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4],
socialRentCap: [155.73, 155.73, 164.87, 174.03, 183.18, 192.35, 201.5],
};

export type nationalAverageType = {
socialRentWeekly: number;
propertyValue: number;
earningsWeekly: number;
};

/**
* National average values
*/
export const NATIONAL_AVERAGES: nationalAverageType = {
socialRentWeekly: 54.62,
propertyValue: 49750,
earningsWeekly: 316.4,
};
192 changes: 88 additions & 104 deletions app/models/tenure/FairholdLandRent.ts
Original file line number Diff line number Diff line change
@@ -1,88 +1,91 @@
import { Fairhold } from "../Fairhold";
import { Mortgage } from "../Mortgage";
import { MONTHS_PER_YEAR } from "../constants";

interface FairholdLandRentParams {
averageRentYearly: number;
averagePrice: number;
newBuildPrice: number;
depreciatedBuildPrice: number;
landPrice: number;
incomeYearly: number;
affordabilityThresholdIncomePercentage: number;
propertyPriceGrowthPerYear: number;
constructionPriceGrowthPerYear: number;
yearsForecast: number;
maintenanceCostPercentage: number;
incomeGrowthPerYear: number;
rentGrowthPerYear: number;
fairhold: Fairhold;
}

export class FairholdLandRent {
depreciatedHouseMortgage?: Mortgage; // mortgage on the depreciated house
discountedLandRentMonthly?: number; // discounted land rent
lifetime?: {
maintenanceCost: number;
fairholdRentLand: number;
houseMortgagePaymentYearly: number;
}[]; // lifetime object with projections
constructor({
averageRentYearly, // average rent per year
averagePrice, // average price of the property
newBuildPrice, // new build price of the property
depreciatedBuildPrice, // depreciated building price
landPrice, // land price
incomeYearly, // yearly income per household
propertyPriceGrowthPerYear, // 5% per year
constructionPriceGrowthPerYear, // 2.5% per year
yearsForecast, // 40 years
maintenanceCostPercentage,
incomeGrowthPerYear, // 4% per year income growth
rentGrowthPerYear, // rent growth per year
}: {
averageRentYearly: number;
averagePrice: number;
newBuildPrice: number;
depreciatedBuildPrice: number;
landPrice: number;
incomeYearly: number;
affordabilityThresholdIncomePercentage: number;
propertyPriceGrowthPerYear: number;
constructionPriceGrowthPerYear: number;
yearsForecast: number;
maintenanceCostPercentage: number;
incomeGrowthPerYear: number;
rentGrowthPerYear: number;
fairhold: Fairhold;
}) {
this.calculateMortgage(depreciatedBuildPrice);
this.calculateLifetime(
averagePrice,
newBuildPrice,
landPrice,
incomeYearly,
averageRentYearly,
yearsForecast,
propertyPriceGrowthPerYear,
constructionPriceGrowthPerYear,
incomeGrowthPerYear,
rentGrowthPerYear,
maintenanceCostPercentage
);
}
type Lifetime = {
maintenanceCost: number;
fairholdRentLand: number;
houseMortgagePaymentYearly: number;
}[];

calculateMortgage(depreciatedBuildPrice: number) {
export class FairholdLandRent {
/** Mortgage on the depreaciated value of the house */
depreciatedHouseMortgage: Mortgage;
/** discounted value of the monthly land rent according to fairhold */
discountedLandRentMonthly: number;
/** lifetime projections of the tenure model */
lifetime: Lifetime;

constructor(params: FairholdLandRentParams) {
this.depreciatedHouseMortgage = new Mortgage({
propertyValue: depreciatedBuildPrice,
propertyValue: params.depreciatedBuildPrice,
});

this.discountedLandRentMonthly =
this.calculateDiscountedLandRentMonthly(params);
this.lifetime = this.calculateLifetime(params);
}

calculateLifetime(
averagePrice: number,
newBuildPrice: number,
landPrice: number,
incomeYearly: number,
averageRentYearly: number,
yearsForecast: number,
propertyPriceGrowthPerYear: number,
constructionPriceGrowthPerYear: number,
incomeGrowthPerYear: number,
rentGrowthPerYear: number,
maintenanceCostPercentage: number
) {
private calculateDiscountedLandRentMonthly({
incomeYearly,
averageRentYearly,
landPrice,
averagePrice,
}: FairholdLandRentParams) {
const marketRentAffordability = incomeYearly / averageRentYearly;
const landToTotalRatio = landPrice / averagePrice;
const averageRentLandMonthly =
(averageRentYearly / MONTHS_PER_YEAR) * landToTotalRatio;

const fairholdLandRent = new Fairhold({
affordability: marketRentAffordability,
landPriceOrRent: averageRentLandMonthly,
});
const discountedLandRentMonthly =
fairholdLandRent.calculateDiscountedPriceOrRent();

return discountedLandRentMonthly;
}
private calculateLifetime({
averagePrice,
newBuildPrice,
landPrice,
incomeYearly,
averageRentYearly,
yearsForecast,
propertyPriceGrowthPerYear,
constructionPriceGrowthPerYear,
incomeGrowthPerYear,
rentGrowthPerYear,
maintenanceCostPercentage,
}: FairholdLandRentParams) {
// initialize the variables that are going to be iterated
let averagePriceIterative = averagePrice;
let newBuildPriceIterative = newBuildPrice;
let landPriceIterative = landPrice;
let landToTotalRatioIterative = landPrice / averagePrice;
let incomeIterative = incomeYearly; // set the current income
let averageRentYearlyIterative = averageRentYearly; // yearly rent
let incomeIterative = incomeYearly;
let averageRentYearlyIterative = averageRentYearly;
let averageRentLandYearlyIterative =
averageRentYearlyIterative * landToTotalRatioIterative; // yearly rent for land
let affordabilityIterative = averageRentYearlyIterative / incomeIterative; // affordability
averageRentYearlyIterative * landToTotalRatioIterative;
let affordabilityIterative = averageRentYearlyIterative / incomeIterative;
let maintenanceCostIterative =
maintenanceCostPercentage * newBuildPriceIterative;

Expand All @@ -92,56 +95,37 @@ export class FairholdLandRent {
}).calculateDiscountedPriceOrRent(); // calculate the discounted land rent
this.discountedLandRentMonthly = fairholdRentLandIterative;

interface mortgageBreakdownTypes {
yearlyPayment: number;
cumulativePaid: number;
remainingBalance: number;
}

if (
this.depreciatedHouseMortgage === undefined ||
this.depreciatedHouseMortgage.yearlyPaymentBreakdown === undefined
) {
throw new Error("depreciatedHouseMortgage is undefined");
}

const houseMortgagePaymentYearly = this.depreciatedHouseMortgage
.yearlyPaymentBreakdown as mortgageBreakdownTypes[];
const houseMortgagePaymentYearly =
this.depreciatedHouseMortgage.yearlyPaymentBreakdown;
let houseMortgagePaymentYearlyIterative =
houseMortgagePaymentYearly[0].yearlyPayment; // find the first year

interface lifetimeTypes {
maintenanceCost: number;
fairholdRentLand: number;
houseMortgagePaymentYearly: number;
}

let lifetime: lifetimeTypes[] = [
let lifetime: Lifetime = [
{
maintenanceCost: maintenanceCostIterative,
fairholdRentLand: fairholdRentLandIterative,
houseMortgagePaymentYearly: houseMortgagePaymentYearlyIterative,
},
]; // initialize the forecast
];

for (let i = 0; i < yearsForecast - 1; i++) {
averagePriceIterative =
averagePriceIterative * (1 + propertyPriceGrowthPerYear); // calculate the average price at a given year
averagePriceIterative * (1 + propertyPriceGrowthPerYear);
newBuildPriceIterative =
newBuildPriceIterative * (1 + constructionPriceGrowthPerYear); // calculate the new build price at a given year
landPriceIterative = averagePriceIterative - newBuildPriceIterative; // calculate the land price at agiven year
landToTotalRatioIterative = landPriceIterative / averagePriceIterative; // calculate the land to total ratio
incomeIterative = incomeIterative * (1 + incomeGrowthPerYear); // calculate the current income
newBuildPriceIterative * (1 + constructionPriceGrowthPerYear);
landPriceIterative = averagePriceIterative - newBuildPriceIterative;
landToTotalRatioIterative = landPriceIterative / averagePriceIterative;
incomeIterative = incomeIterative * (1 + incomeGrowthPerYear);
maintenanceCostIterative =
maintenanceCostPercentage * newBuildPriceIterative; // calculate the curretn maintenance cost
maintenanceCostPercentage * newBuildPriceIterative;

averageRentYearlyIterative =
averageRentYearlyIterative * (1 + rentGrowthPerYear); // calculate the current rent
averageRentYearlyIterative * (1 + rentGrowthPerYear);

averageRentLandYearlyIterative =
averageRentYearlyIterative * landToTotalRatioIterative; // yearly rent for land
averageRentYearlyIterative * landToTotalRatioIterative;

let affordabilityIterative = averageRentYearlyIterative / incomeIterative; // affordability
let affordabilityIterative = averageRentYearlyIterative / incomeIterative;

let fairholdRentLandIterative = new Fairhold({
affordability: affordabilityIterative,
Expand All @@ -161,6 +145,6 @@ export class FairholdLandRent {
houseMortgagePaymentYearly: houseMortgagePaymentYearlyIterative,
}); // add the current price to the new build price forecast
}
this.lifetime = lifetime; // save the object
return lifetime; // save the object
}
}
Loading