import numpy as np from multipolyfit import multipolyfit as mpf GROSS_RENT_YIELD: float = 0.03686 RISK_POOL_ALLOCATION: float = 0.01 LOSS_SEVERITY: float = 0.19 RECOVERY_RATE: float = 0.9 MIN_MRE: float = 0.05 def get_default_risk_by_fico(consumer_fico: int, loan_to_value: float) -> float: # remember that the credit score is the first variable and the ltv is the second initial_fico_ltv_risks = [ [620, 0.01], [620, 0.70], [620, 0.85], [620, 0.90], [620, 0.95], [660, 0.01], [660, 0.70], [660, 0.85], [660, 0.90], [660, 0.95], [700, 0.01], [700, 0.70], [700, 0.85], [700, 0.90], [700, 0.95], [740, 0.01], [740, 0.70], [740, 0.85], [740, 0.90], [740, 0.95] ] default_rates_by_credit_score = { 620: 0.0303, 660: 0.0201, 700: 0.0150, 740: 0.0094 } risk_factors_by_credit_score = { 620: [0.20, 0.61, 1.22, 1.48, 1.80], 660: [0.20, 0.62, 1.22, 1.48, 1.82], 700: [0.20, 0.62, 1.22, 1.49, 1.83], 740: [0.20, 0.63, 1.21, 1.47, 1.81] } # We should now fill out the table of default rates with the LTV risk factors included initial_risk_factors_by_ltv_credit_score = [] for fico in risk_factors_by_credit_score: initial_risk_factors_by_ltv_credit_score += list(np.multiply(default_rates_by_credit_score[fico], risk_factors_by_credit_score[fico])) # Compute a line of best fit lobf_coefficients = mpf(initial_fico_ltv_risks, initial_risk_factors_by_ltv_credit_score, 1) # the first coeffient is a constant offset, the second is the credit score factor, and the third is the ltv factor const_offset = lobf_coefficients[0] consumer_fico_factor = (lobf_coefficients[1] * consumer_fico) loan_to_value_factor = (lobf_coefficients[2] * loan_to_value) # now we just sum these up to get the result result = consumer_fico_factor + loan_to_value_factor + const_offset return result def get_risk_pool_health() -> float: return 1.0 def compute_mre(home_value: float, down_payment: float, consumer_fico: int) -> float: if down_payment < 0 or down_payment > 1: raise ValueError('The down_payment must be between 0 and 1') if consumer_fico < 620 or consumer_fico > 850: raise ValueError('The consumer_fico must between 620 and 850') down_payment *= home_value investor_value = home_value - down_payment loan_to_value = float(investor_value/home_value) # the monthly payment is made on the value from the investors monthly_payment = investor_value * (GROSS_RENT_YIELD) / 12.0 # how much is at risk if the occupant "defaults" at_risk_value = home_value * LOSS_SEVERITY # how much is expected to be recovered? recovery_value = at_risk_value * RECOVERY_RATE # get the default rate with the risk-factors dealt with default_rate = get_default_risk_by_fico(consumer_fico, loan_to_value) # get the risk pool allocation risk_pool_allocation = RISK_POOL_ALLOCATION * home_value # we should add the 4 month buffer that effectively halves the default rate, # this is already included in the get_default_risk_by_fico method. income_interruption_buffer = 4.0 * monthly_payment default_rate = get_default_risk_by_fico(consumer_fico, loan_to_value) # the expected loss to the risk pool default_rate_risk_pool_loss = default_rate * risk_pool_allocation # adjust the risk pool loss by the risk pool's health risk_pool_factor = get_risk_pool_health() * default_rate_risk_pool_loss # since we don't actually lose anything in the risk pool, it does not # get counted toward the loss at_risk_value -= risk_pool_factor # start putting together the pieces mre = income_interruption_buffer mre += at_risk_value - recovery_value # make the mre a percentage of the home value mre /= home_value mre = max(mre, MIN_MRE) return mre #if __name__ == '__main__': # print('Computing risk factor for consumer with 660 FICO and 70% LTV') # print(f'Deault Rate: {get_default_risk_by_fico(660, 0.7)}\n') # print('Comuting MRE for 742,500 home with 2.5% down payment and a 680 fico') # print(f' MRE: {compute_mre(742500.0, 0.025*742500.0, 680)}')