Skip to content

Commit

Permalink
docs: improve tsdoc comments for documentation clarity
Browse files Browse the repository at this point in the history
  • Loading branch information
zz-hh-aa committed Nov 27, 2024
1 parent 0d36034 commit 808b99a
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 29 deletions.
21 changes: 21 additions & 0 deletions app/models/Fairhold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,25 @@ const PLATEAU = 0.15

type ConstructorParams = Pick<Fairhold, "affordability" | "landPriceOrRent">;

/** The Fairhold class is a generic class
* (meaning it will calculate a discount regardless if it is given a purchase price or monthly rent).
* It is instantiated twice,
* once each for `FairholdLandPurchase` and `FairholdLandRent`
*/
export class Fairhold {
/** Affordability is calculated as monthly housing cost / GDHI */
public affordability: number;
/** Depending on whether calculating `FairholdLandPurchase` or `FairholdLandRent`,
* pass the relevant figure (open market residual land value or estimated portion of monthly market rent
* that goes towards location)
*/
public landPriceOrRent: number;
/** The Fairhold discount is a multiplier, so Fairhold prices are `discountLand * open market value`
*/
public discountLand: number;
/** When the class is instantiated for `FairholdLandPurchase`, this is the discounted up-front land purchase price;
* when instantiated for `FairholdLandPurchase`, this is the discounted monthly community ground rent
*/
public discountedLandPriceOrRent: number;

constructor(params: ConstructorParams) {
Expand All @@ -20,11 +35,17 @@ export class Fairhold {
this.discountedLandPriceOrRent = this.calculateDiscountedPriceOrRent();
}

/** Our formula is linear; the more expensive an area is, the lower the generated price multiplier is;
* it plateaus at .15 of market rate
*/
private calculateFairholdDiscount() {
const discountLand = math.max(MULTIPLIER * this.affordability + OFFSET, PLATEAU)
return discountLand;
}

/** Multiplies market land price or rent by the discountLand multiplier;
* in the event that land values are effectively negative, land price will be £1
*/
private calculateDiscountedPriceOrRent() {
if (this.landPriceOrRent < 0) {
// TODO: Set a nominal value (Check with Ollie)
Expand Down
23 changes: 14 additions & 9 deletions app/models/ForecastParameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,25 @@ export interface ForecastParameters {
affordabilityThresholdIncomePercentage: number;
}

/** Parameters for forecasting changing costs over time,
* all values except years are percentages represented in decimal form
*/
export const DEFAULT_FORECAST_PARAMETERS: ForecastParameters = {
maintenancePercentage: 0.02, // percentage maintenance cost
incomeGrowthPerYear: 0.04, // 4% income growth per year
constructionPriceGrowthPerYear: 0.025, // 2.5%
rentGrowthPerYear: 0.03, // 3%
propertyPriceGrowthPerYear: 0.05, // 5%
yearsForecast: 40, // 40 years
affordabilityThresholdIncomePercentage: 0.35, // percentage of income to afford rent or purchase
maintenancePercentage: 0.02,
incomeGrowthPerYear: 0.04,
constructionPriceGrowthPerYear: 0.025,
rentGrowthPerYear: 0.03,
propertyPriceGrowthPerYear: 0.05,
/** The number of years to forecast values over */
yearsForecast: 40,
/** The threshold of GDHI at which housing is no longer considered affordable */
affordabilityThresholdIncomePercentage: 0.35,
} as const;

/**
* Creates forecast parameters
* @param maintenancePercentage - Maintenance spend value from form (0.015 | 0.02 | 0.0375)
* @returns ForecastParameters with updated maintenance cost
* @param maintenancePercentage - Maintenance spend value, user input from form
* @returns ForecastParameters with updated maintenance spend (overwrites default)
*/
export function createForecastParameters(maintenancePercentage: number): ForecastParameters {

Expand Down
12 changes: 7 additions & 5 deletions app/models/Household.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ForecastParameters } from "./ForecastParameters";
import { socialRentAdjustmentTypes } from "../data/socialRentAdjustmentsRepo";
import { Lifetime, LifetimeParams } from "./Lifetime";

/** Assumed number of heads per-house */
const HOUSE_MULTIPLIER = 2.4;

type ConstructorParams = Pick<
Expand All @@ -21,6 +22,7 @@ type ConstructorParams = Pick<
housePriceIndex: number;
};

/** The 'parent' class; when instantiated, it instantiates all other relevant classes, including `Property` */
export class Household {
public incomePerPersonYearly: number;
public gasBillYearly: number;
Expand Down Expand Up @@ -93,17 +95,17 @@ export class Household {

const fairholdLandRent = new FairholdLandRent({
averageRentYearly: averageRentYearly,
averagePrice: this.property.averageMarketPrice, // average price of the property
averagePrice: this.property.averageMarketPrice,
newBuildPrice: this.property.newBuildPrice,
depreciatedBuildPrice: this.property.depreciatedBuildPrice, // depreciated building price
landPrice: this.property.landPrice, // land price
incomeYearly: this.incomeYearly, // income
depreciatedBuildPrice: this.property.depreciatedBuildPrice,
landPrice: this.property.landPrice,
incomeYearly: this.incomeYearly,
forecastParameters: this.forecastParameters,

fairhold: new Fairhold({
affordability: marketRent.affordability,
landPriceOrRent: averageRentYearly,
}), // fairhold object
}),

marketPurchase: marketPurchase
});
Expand Down
12 changes: 8 additions & 4 deletions app/models/Lifetime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// imports (eg constants)
import { MarketPurchase } from "./tenure/MarketPurchase";
import { MarketRent } from "./tenure/MarketRent";
import { FairholdLandPurchase } from "./tenure/FairholdLandPurchase";
Expand All @@ -7,7 +6,6 @@ import { Fairhold } from "./Fairhold";
import { Property } from "./Property";
import { MONTHS_PER_YEAR } from "./constants";

// interfaces and types
export interface LifetimeParams {
marketPurchase: MarketPurchase;
marketRent: MarketRent;
Expand Down Expand Up @@ -39,14 +37,20 @@ export interface LifetimeData {
// gasBillYearly: number;
[key: number]: number;
}

/** The `Lifetime` class calculates yearly spend on housing over a lifetime (set by `yearsForecast`).
* Instead of storing lifetime data within each tenure class itself,
* `Lifetime` is stored in its own class (to prevent excess duplication of properties like `incomeYearly`).
*/
export class Lifetime {
public lifetimeData: LifetimeData[];

constructor(params: LifetimeParams) {
this.lifetimeData = this.calculateLifetime(params);
}


/** The function loops through and calculates all values for period set by yearsForecast,
* pushing the results to the lifetime array (one object per-year)
*/
private calculateLifetime(params: LifetimeParams): LifetimeData[] {
const lifetime: LifetimeData[] = [];

Expand Down
4 changes: 4 additions & 0 deletions app/models/Mortgage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ type MortgageBreakdown = {
remainingBalance: number;
}[];

/** The `Mortgage` class is instantiated each time a mortgage needs to be calculated,
* meaning per-type of property, eg land or house, per-tenure
*/
export class Mortgage {
propertyValue: number;
/**
Expand All @@ -33,6 +36,7 @@ export class Mortgage {
*/
principal: number;
monthlyPayment: number;
/** This includes principal and interest */
totalMortgageCost: number;
yearlyPaymentBreakdown: MortgageBreakdown;
totalInterest: number;
Expand Down
5 changes: 3 additions & 2 deletions app/models/Property.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export class Property {
numberOfBedrooms: number;
age: number;
/**
* Size of the house in squares meters
* Size of the house in square meters
*/
size: number;
maintenancePercentage: MaintenancePercentage;
Expand All @@ -42,7 +42,8 @@ export class Property {
averageMarketPrice: number;
itl3: string;
/**
* Price of the house if it was new
* Price of the house if it was new, used for residual land value calculations
* and for valuing the house alone in marketPurchase
*/
newBuildPrice: number;
/**
Expand Down
8 changes: 6 additions & 2 deletions app/models/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type bedWeightsAndCapsType = {
};

/**
* multiplying value weight and social rent cap for a property based on number of bed rooms
* This is used to weight social rent values by property size based on number of bedrooms
*/
export const BED_WEIGHTS_AND_CAPS: bedWeightsAndCapsType = {
numberOfBedrooms: [0, 1, 2, 3, 4, 5, 6],
Expand All @@ -23,12 +23,16 @@ export type nationalAverageType = {
};

/**
* National average values
* National averages from 1999 and 2000 (from MHCLG), used as inputs for calculating social rent
*/
export const NATIONAL_AVERAGES: nationalAverageType = {
socialRentWeekly: 54.62,
propertyValue: 49750,
earningsWeekly: 316.4,
};

/**
* Maintenance levels are percentages (represented as decimals),
* figures from our own model
*/
export const MAINTENANCE_LEVELS = [0.015, 0.02, 0.0375] as const;
6 changes: 5 additions & 1 deletion app/models/tenure/FairholdLandPurchase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ interface FairholdLandPurchaseParams {
marketPurchase: MarketPurchase;
}

/** `FairholdLandPurchase` needs different params to FairholdLandRent,
* which is why they are separate classes that both instantiate an instance of Fairhold.
* Where `FairholdLandRent` uses other classes (eg `MarketPurchase`, `ForecastParameters`), they are passed in*/
export class FairholdLandPurchase {
params: FairholdLandPurchaseParams;
discountedLandPrice: number;
discountedLandMortgage: Mortgage;
depreciatedHouseMortgage: Mortgage;
/** All tenure classes have an `interestPaid` property, summed from `Mortgage.totalInterest` (if more than one `Mortgage` class) */
interestPaid: number;
/** interest saved relative to market purchase, pounds */
/** Interest saved relative to `MarketPurchase`, pounds */
interestSaved: number;

constructor(params: FairholdLandPurchaseParams) {
Expand Down
10 changes: 7 additions & 3 deletions app/models/tenure/FairholdLandRent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ interface FairholdLandRentParams {
marketPurchase: MarketPurchase;
}

/** `FairholdLandRent` needs different params to `FairholdLandPurchase`,
* which is why they are separate classes that both instantiate an instance of `Fairhold`.
* Where `FairholdLandRent` uses other classes (eg `MarketPurchase`, `ForecastParameters`), they are passed in*/
export class FairholdLandRent {
params: FairholdLandRentParams;
/** Mortgage on the depreciated value of the house */
depreciatedHouseMortgage: Mortgage;
/** discounted value of the monthly land rent according to fairhold */
discountedLandRentMonthly: number;
/** All tenure classes have an `interestPaid` property, summed from `Mortgage.totalInterest` (if more than one `Mortgage` class) */
interestPaid: number;
/** interest saved relative to market purchase, pounds */
/** Interest saved relative to `MarketPurchase`, pounds */
interestSaved: number;

constructor(params: FairholdLandRentParams) {
Expand All @@ -49,6 +51,8 @@ export class FairholdLandRent {
averagePrice,
}: FairholdLandRentParams) {
const marketRentAffordability = incomeYearly / averageRentYearly;
/*TODO: landToTotalRatio is calculated elsewhere too, eg when instantiating socialRent in Property...
can we just calculate it once?*/
const landToTotalRatio = landPrice / averagePrice;
const averageRentLandMonthly =
(averageRentYearly / MONTHS_PER_YEAR) * landToTotalRatio;
Expand Down
3 changes: 3 additions & 0 deletions app/models/tenure/MarketPurchase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ interface MarketPurchaseParams {
forecastParameters: ForecastParameters;
}

// TODO: decide on language to use (eg freeholdPurchase?)
export class MarketPurchase {
params: MarketPurchaseParams;
/** Uses the summed mortgages to calculate percentage of GDHI spent on housing monthly */
public affordability: number;
public houseMortgage: Mortgage;
public landMortgage: Mortgage;
/** All tenure classes have an `interestPaid` property, summed from `Mortgage.totalInterest` (if more than one Mortgage class) */
public interestPaid: number;

constructor(params: MarketPurchaseParams) {
Expand Down
1 change: 1 addition & 0 deletions app/models/tenure/MarketRent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class MarketRent {
averageRentYearly,
}: MarketRentParams) {
const averageRentMonthly = averageRentYearly / MONTHS_PER_YEAR;
// TODO: landToTotalRatio is calculated multiple times in multiple places, can we just do it once?
const landToTotalRatio = landPrice / averagePrice;
const averageRentLandMonthly = averageRentMonthly * landToTotalRatio;
const averageRentHouseMonthly = averageRentMonthly - averageRentLandMonthly;
Expand Down
6 changes: 3 additions & 3 deletions app/models/tenure/SocialRent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export class SocialRent {
/** adjustment factors that take into account the increase of living cost */
socialRentAdjustments;
housePriceIndex;
adjustedSocialRentMonthly: number; //adjusted social rent monthly
socialRentMonthlyLand: number; // social rent to pay the land
socialRentMonthlyHouse: number; // social rent monthly House
adjustedSocialRentMonthly: number;
socialRentMonthlyLand: number;
socialRentMonthlyHouse: number;
constructor(params: SocialRentParams) {
this.socialRentAverageEarning = params.socialRentAverageEarning;
this.socialRentAdjustments = params.socialRentAdjustments;
Expand Down

0 comments on commit 808b99a

Please sign in to comment.