# import matplotlib.pyplot as plt # import pandas as pd #import numpy as np from statistics import mean # from simple_colors import * import math from otree.api import * author = 'Xiaomeng' doc = """ This is a loan simulator app. """ def simulator(family_size,loan_amount,interest_rate,graduate_yearly_income, income_growth_rate): # Set up the basic variables and functions print('--------------------------------------') print('SET UP') # loan_amount = float(input("Enter the principal amount: ")) # interest_rate = float(input("Enter the interest rate (as a decimal): ")) ############ # decomment the following three input argument to play around the simulator # graduate_yearly_income = float(input("Enter the initial yearly income: ")) # income_growth_rate = float(input("Enter the income growth rate (as a decimal): ")) print('') # standard_payment_years = int(input("Enter the standard fixed loan repayment years: ")) # IBR_loan_term_forgiveness = int(input("Enter the IRB forgiveness years: ")) # IBR_loan_discretionary_income = float(input("Enter the percentage above poverty line to define IBR discretionary income (as a decimal): ")) # SAVE_loan_discretionary_income = float(input("Enter the percentage above poverty line to define SAVE discretionary income (as a decimal): ")) # graduate_yearly_income = 20000 # income_growth_rate = 0.1 standard_payment_years = 10 IBR_loan_term_forgiveness = 20 IBR_loan_discretionary_income = 1.5 SAVE_loan_discretionary_income = 2.25 # print("Loan amount | Forgiveness | Interest rate") # print(f"${loan_amount:.2f} | {IBR_loan_term_forgiveness:.2f} years | {interest_rate:.2f}") # print("Initial income | Income growth") # print(f"${graduate_yearly_income:.2f} | {income_growth_rate:.2f}") poverty_lines_base = [14580, 15019.35093, 15346.40587, 15661.49017, 15984.41239, 16333.74602, 16702.03316, 17081.10338, 17469.43658, 17867.3178, 18275.31706, 18692.63296, 19119.47824, 19556.07052, 20002.63235, 20459.3914, 20926.58053, 21404.43789, 21893.20709, 22393.13732, 22904.48343] poverty_lines = [x+(family_size-1)*5140 for x in poverty_lines_base] results = [] # Calculate standard monthly payment def calculate_standard_payment(principal, interest_rate): return principal * (interest_rate / 12) / (1 - (1 + interest_rate / 12) ** (-standard_payment_years * 12)) # Calculate IBR monthly payment def calculate_loan_payment(discretionary_income, standard_payment): return max(0, min((discretionary_income * 0.10) / 12, standard_payment)) # Calculate SAVE monthly payment def calculate_save_loan_payment(discretionary_income, standard_payment): return max(0, (discretionary_income * 0.10) / 12) # Calculate flexiable SAVE monthly payment def calculate_flex_save_loan_payment(flex_save_monthly_payment, discretionary_income, standard_payment, income): return max(0, min((discretionary_income * flex_save_monthly_payment) / 12, income / 120)) # Calculate IBR monthly interest def IRB_interest_amount(principal, balance, monthly_payment, standard_payment, interest_rate): if monthly_payment < standard_payment and principal < balance: return principal * interest_rate / 12 else: return balance * interest_rate / 12 # Calculate SAVE forgiveness years def SAVE_forgiveness_year(principal): if principal <= 12000: return 10 elif principal <= 22000: return math.floor((principal - 2000) / 1000) else: return 20 standard_payment = calculate_standard_payment(loan_amount, interest_rate) SAVE_loan_term_forgiveness = SAVE_forgiveness_year(loan_amount) pv_income = [] graduate_yearly_income_pv = graduate_yearly_income overall_graduate_yearly_income = graduate_yearly_income overall_income = [] for year in range(30): overall_income.append(overall_graduate_yearly_income) overall_graduate_yearly_income *= (1 + income_growth_rate) for year in range(40): graduate_yearly_income_pv *= 0.95 * (1 + income_growth_rate) pv_income.append(graduate_yearly_income_pv) standard_net_income_year_list = [] graduate_yearly_income_standard = graduate_yearly_income for year in range(10): standard_net_income_year_list.append(graduate_yearly_income_standard - standard_payment * 12) graduate_yearly_income_standard *= (1 + income_growth_rate) print('Present value of 40 years income: $', sum(pv_income)) # %% print('--------------------------------------') print('Standard 10 year student loan') print('Monthly payment: $', round(standard_payment, 2)) print('Total payments paid: $', round(standard_payment * 10 * 12, 2)) print('Average payments in') print(' Years 1-5: $', round(standard_payment * 12, 2)) print(' Years 6-10: $', round(standard_payment * 12, 2)) print('Net annual income net of debt repayment in') print(' Years 1-5: $', round(mean(standard_net_income_year_list[0:5]), 2)) print(' Years 6-10: $', round(mean(standard_net_income_year_list[5:10]), 2)) print('--------------------------------------') print('IBR student loan') # Initialize lists to store the data years = [] months = [] principal_list = [] principal_year_list = [] original_principal_list = [] original_principal_year_list = [] interest_list = [] interest_year_list = [] monthly_payment_list = [] monthly_payment_year_list = [] outstanding_list = [] outstanding_year_list = [] income_year_list = [] net_income_year_list = [] graduate_yearly_income_t = graduate_yearly_income for year in range(max(IBR_loan_term_forgiveness, SAVE_loan_term_forgiveness)): income_year_list.append(graduate_yearly_income_t / 12) graduate_yearly_income_t *= (1 + income_growth_rate) # %% # Calculate the loan repayment schedule initial_yearly_income = graduate_yearly_income remaining_balance = loan_amount emi_counter = 1 initial_discretionary_income = initial_yearly_income - (IBR_loan_discretionary_income * poverty_lines[0]) initial_monthly_payment = (initial_discretionary_income * 0.10) / 12 if initial_monthly_payment > standard_payment: print('Not available as payment under this exceeds Standard loan payment') IBR_index = 'UNAVAILABLE since monthly payment exceeds standard plan' else: IBR_index = '' for year in range(IBR_loan_term_forgiveness): discretionary_income = initial_yearly_income - (IBR_loan_discretionary_income * poverty_lines[year]) monthly_payment = calculate_loan_payment(discretionary_income, standard_payment) net_income_year_list.append(initial_yearly_income / 12 - monthly_payment) initial_yearly_income *= (1 + income_growth_rate) monthly_payment_year_list.append(monthly_payment) for month in range(12): interest_amount = IRB_interest_amount(loan_amount, remaining_balance, monthly_payment, standard_payment, interest_rate) if monthly_payment < interest_amount: interest_payment = monthly_payment elif remaining_balance > loan_amount: interest_payment = monthly_payment else: interest_payment = interest_amount principal_payment = monthly_payment - interest_amount remaining_balance -= principal_payment if remaining_balance < loan_amount: original_principal_payment = loan_amount - remaining_balance else: original_principal_payment = 0 outstanding = remaining_balance years.append(f"{year + 1}") months.append(f"{month + 1}") principal_list.append(principal_payment) original_principal_list.append(original_principal_payment) interest_list.append(interest_payment) monthly_payment_list.append(monthly_payment) outstanding_list.append(outstanding) emi_counter += 1 if outstanding <= 0: break outstanding_year_list.append(max(0, outstanding)) principal_year_list.append(sum(principal_list)) original_principal_year_list.append(original_principal_payment) interest_year_list.append(sum(interest_list)) if outstanding <= 0: break print('Years with positive payment', len(monthly_payment_year_list)) print('Monthly payment from', round(monthly_payment_list[1], 2), 'to', round(monthly_payment_list[-1], 2)) print('Forgiveness Amount: $', round(outstanding_year_list[-1], 2)) print('Total payments paid: $', round(original_principal_year_list[-1] + interest_year_list[-1], 2)) print('Average payments in') print(' Years 1-5: $', round(mean(monthly_payment_year_list[0:5]) * 12, 2)) print(' Years 6-10: $', round(mean(monthly_payment_year_list[5:10]) * 12, 2)) if len(monthly_payment_year_list[10:]) > 0: print(' Years 10+: $', round(mean(monthly_payment_year_list[10:]) * 12, 2)) else: print(' Years 10+: /') IBR_forgiven_amt = str(round(outstanding_year_list[-1],2)) outstanding_year_list[-1] = 0 print('Net annual income net of debt repayment in') print(' Years 1-5: $', round(mean(net_income_year_list[0:5]) * 12, 2)) print(' Years 6-10: $', round(mean(net_income_year_list[5:10]) * 12, 2)) if len(monthly_payment_year_list[10:]) > 0: print(' Years 10+: $', round(mean(net_income_year_list[10:]) * 12, 2)) else: print(' Years 10+: /') # x = range(min(len(monthly_payment_year_list), IBR_loan_term_forgiveness)) # plt.step(x, monthly_payment_year_list, c='blue') # plt.axis([0, max(x) + 1, 0, standard_payment + 50]) # plt.xticks(np.arange(min(x), max(x) + 1, 1)) # plt.title('Monthly payment amount') # plt.show() # # x = range(min(len(monthly_payment_year_list), IBR_loan_term_forgiveness)) # plt.step(x, net_income_year_list, c='blue') # plt.xticks(np.arange(min(x), max(x) + 1, 1)) # plt.title('Monthly income net of debt repayment') # plt.show() # # plt.plot(x, outstanding_year_list, label="Remaining Balance", c='blue') # plt.plot(x, original_principal_year_list, label='Principal Paid', c='green') # plt.plot(x, interest_year_list, label='Interest Paid', c='red') # plt.xticks(np.arange(min(x), max(x) + 1, 1)) # plt.legend() # plt.title('Loan Amortization Chart') # plt.show() print(len(monthly_payment_list)) print('--------------------------------------') print('SAVE student loan') # Initialize lists to store the data save_years = [] save_months = [] save_principal_list = [] save_principal_year_list = [] save_original_principal_list = [] save_original_principal_year_list = [] save_interest_list = [] save_interest_year_list = [] save_monthly_payment_list = [] save_monthly_payment_year_list = [] save_outstanding_list = [] save_outstanding_year_list = [] save_emi_counter = 1 save_net_income_year_list = [] # Calculate the loan repayment schedule save_remaining_balance = loan_amount save_initial_yearly_income = graduate_yearly_income emi_counter = 1 for year in range(SAVE_loan_term_forgiveness): discretionary_income = save_initial_yearly_income - (SAVE_loan_discretionary_income * poverty_lines[year]) monthly_payment = calculate_save_loan_payment(discretionary_income, standard_payment) save_net_income_year_list.append(save_initial_yearly_income / 12 - monthly_payment) save_initial_yearly_income *= (1 + income_growth_rate) save_monthly_payment_year_list.append(monthly_payment) for month in range(12): interest_amount = save_remaining_balance * interest_rate / 12 interest_payment = min(monthly_payment, interest_amount) principal_payment = monthly_payment - interest_payment save_remaining_balance -= principal_payment if save_remaining_balance < loan_amount: save_original_principal_payment = loan_amount - save_remaining_balance else: save_original_principal_payment = 0 outstanding = save_remaining_balance save_years.append(f"{year + 1}") save_months.append(f"{month + 1}") save_principal_list.append(principal_payment) save_original_principal_list.append(save_original_principal_payment) save_interest_list.append(interest_payment) save_monthly_payment_list.append(monthly_payment) save_outstanding_list.append(outstanding) save_emi_counter += 1 if outstanding <= 0: break save_outstanding_year_list.append(max(0, outstanding)) save_principal_year_list.append(sum(save_principal_list)) save_original_principal_year_list.append(save_original_principal_payment) save_interest_year_list.append(sum(save_interest_list)) if outstanding <= 0: break print('Years with positive payment', len(save_monthly_payment_year_list)) print('Monthly payment from', round(save_monthly_payment_list[0], 2), 'to', round(save_monthly_payment_list[-1], 2)) print('Forgiveness Amount: $', round(save_outstanding_year_list[-1], 2)) print('Total payments paid: $', round(save_original_principal_year_list[-1] + save_interest_year_list[-1], 2)) print('Average payments in') print(' Years 1-5: $', round(mean(save_monthly_payment_year_list[0:5]) * 12, 2)) if len(save_monthly_payment_year_list[5:]) > 0: print(' Years 6-10: $', round(mean(save_monthly_payment_year_list[5:10])*12,2)) else: print(' Years 6-10: /') if len(save_monthly_payment_year_list[10:]) > 0: print(' Years 10+: $', round(mean(save_monthly_payment_year_list[10:]) * 12, 2)) else: print(' Years 10+: /') print('Net annual income net of debt repayment in') print(' Years 1-5: $', round(mean(save_net_income_year_list[0:5]) * 12, 2)) if len(save_monthly_payment_year_list[5:]) > 0: print(' Years 6-10: $', round(mean(save_net_income_year_list[5:10])*12,2)) else: print(' Years 6-10: /') if len(save_monthly_payment_year_list[10:]) > 0: print(' Years 10+: $', round(mean(save_net_income_year_list[10:]) * 12, 2)) else: print(' Years 10+: /') save_forgiven = save_outstanding_year_list[-1] save_outstanding_year_list[-1] = 0 job_A_overall_income = overall_income job_A_IBR_net = [i * 12 for i in net_income_year_list] job_A_save_net = [i * 12 for i in save_net_income_year_list] year1_5_net_std = mean((standard_net_income_year_list+overall_income[len(standard_net_income_year_list):])[:5]) year6_10_net_std = mean((standard_net_income_year_list + overall_income[len(standard_net_income_year_list):])[5:10]) year11_net_std = mean((standard_net_income_year_list + overall_income[len(standard_net_income_year_list):])[10:20]) year20_net_std = mean((standard_net_income_year_list + overall_income[len(standard_net_income_year_list):])[20:30]) if IBR_index == '': IBR_year = str(round(len(monthly_payment_year_list),2)) IBR_total_payment = str(round(original_principal_year_list[-1]+interest_year_list[-1],2)) IBR_1_5_net = str(round(mean((job_A_IBR_net+job_A_overall_income[len(job_A_IBR_net):])[:5]),2)) IBR_6_10_net = str(round(mean((job_A_IBR_net+job_A_overall_income[len(job_A_IBR_net):])[5:10]),2)) IBR_11_20_net = str(round(mean((job_A_IBR_net+job_A_overall_income[len(job_A_IBR_net):])[10:20]),2)) IBR_20_net = str(round(mean((job_A_IBR_net+job_A_overall_income[len(job_A_IBR_net):])[20:30]),2)) IBR_15_payment = str(round(mean(monthly_payment_year_list[0:5]) * 12,2)) IBR_610_payment = str(round(mean(monthly_payment_year_list[5:10]) * 12,2)) if len(monthly_payment_year_list[10:]) > 0: IBR_11_payment = str(round(mean(monthly_payment_year_list[10:]) * 12,2)) else: IBR_11_payment = 'N/A' else: IBR_year = 'N/A' IBR_forgiven_amt = 'N/A' IBR_total_payment= 'N/A' IBR_1_5_net= 'N/A' IBR_6_10_net= 'N/A' IBR_11_20_net= 'N/A' IBR_20_net= 'N/A' IBR_15_payment= 'N/A' IBR_610_payment= 'N/A' IBR_11_payment = 'N/A' SAVE_year = len(save_monthly_payment_year_list) save_total_payment = save_original_principal_year_list[-1]+save_interest_year_list[-1] save_1_5_net = mean((job_A_save_net + job_A_overall_income[len(job_A_save_net):])[:5]) save_6_10_net = mean((job_A_save_net + job_A_overall_income[len(job_A_save_net):])[5:10]) save_11_20_net = mean((job_A_save_net + job_A_overall_income[len(job_A_save_net):])[10:20]) save_20_net = mean((job_A_save_net + job_A_overall_income[len(job_A_save_net):])[20:30]) save_15_payment = mean(save_monthly_payment_year_list[0:5]) * 12 if len(save_monthly_payment_year_list[5:]) > 0: save_610_payment = mean(save_monthly_payment_year_list[5:10]) * 12 else: save_610_payment = 0 if len(save_monthly_payment_year_list[10:]) > 0: save_11_payment = mean(save_monthly_payment_year_list[10:]) * 12 else: save_11_payment = 0 return year1_5_net_std, year6_10_net_std,year11_net_std,year20_net_std,IBR_index,IBR_year,IBR_forgiven_amt,IBR_total_payment,IBR_1_5_net,IBR_6_10_net,IBR_11_20_net,IBR_20_net,IBR_15_payment,IBR_610_payment,IBR_11_payment,SAVE_year,save_forgiven,save_total_payment,save_1_5_net,save_6_10_net,save_11_20_net,save_20_net,save_15_payment,save_610_payment,save_11_payment # overall_income, standard_net_income_year_list, [i * 12 for i in net_income_year_list], [i * 12 for i in save_net_income_year_list]) class Constants(BaseConstants): name_in_url = 'workshop_app' players_per_group = None num_rounds = 20 ################################################## quadratic= 1 investment = 2 ################################## economics = 1 other = 2 ########################## job = 1 health = 2 ############################ Fixed_Amount_1 = 1 Random_1 = 2 ################################# Fixed_Amount_2 = 1 Random_2 = 2 ################################## Fixed_Amount_3 = 1 Random_3 = 2 ###################################### Fixed_Amount_4 = 1 Random_4 = 2 ############################# test_str = "You would have received a random amount of " ###################################### Time = 5 #################################################################################################### class Subsession(BaseSubsession): pass class Group(BaseGroup): pass ################################################################## ################################################################## class Player(BasePlayer): family = models.FloatField(min=1) principal = models.FloatField(min=5000,max=100000) interest_rate = models.FloatField(min=0, max=1) income = models.FloatField(min=15000,max=400000) growth = models.FloatField(min=0, max=1) std_total = models.FloatField() std_year_1_5_payment = models.FloatField() std_year_6_10_payment = models.FloatField() std_year_1_5_net_income = models.FloatField() std_year_6_10_net_income = models.FloatField() year11_net_std = models.FloatField() year20_net_std = models.FloatField() IBRind = models.StringField() IBR_year = models.StringField() IBR_forgiven = models.StringField() IBR_total_payment = models.StringField() IBR_1_5_net = models.StringField() IBR_6_10_net = models.StringField() IBR_11_20_net = models.StringField() IBR_20_net = models.StringField() IBR_15_payment = models.StringField() IBR_610_payment =models.StringField() IBR_11_payment = models.StringField() SAVE_year = models.FloatField() save_forgiven = models.FloatField() save_total_payment = models.FloatField() save_1_5_net = models.FloatField() save_6_10_net = models.FloatField() save_11_20_net = models.FloatField() save_20_net = models.FloatField() save_15_payment = models.FloatField() save_610_payment = models.FloatField() save_11_payment = models.FloatField() # ############################################################################################################################### class Input_Quadratic(Page): form_model = 'player' form_fields = ['family','principal','interest_rate','income','growth'] @staticmethod def before_next_page(player: Player, timeout_happened): year1_5_net_std, year6_10_net_std, year11_net_std, year20_net_std,IBR_index,IBR_year, IBR_forgiven, IBR_total_payment, IBR_1_5_net, IBR_6_10_net, IBR_11_20_net, IBR_20_net, IBR_15_payment, IBR_610_payment, IBR_11_payment,SAVE_year, save_forgiven, save_total_payment, save_1_5_net, save_6_10_net, save_11_20_net, save_20_net, save_15_payment, save_610_payment, save_11_payment = simulator(player.family,player.principal,player.interest_rate,player.income, player.growth) std_monthly = player.principal * (player.interest_rate / 12) / (1 - (1 + player.interest_rate / 12) ** (-10 * 12)) player.std_total = round(std_monthly*120, 1) player.std_year_1_5_payment = round(std_monthly*12, 1) player.std_year_6_10_payment = round(std_monthly*12, 1) player.std_year_1_5_net_income = round(year1_5_net_std,1) player.std_year_6_10_net_income = round(year6_10_net_std,1) player.year11_net_std = round(year11_net_std,1) player.year20_net_std = round(year20_net_std,1) player.IBRind = IBR_index player.IBR_year = IBR_year player.IBR_forgiven = IBR_forgiven player.IBR_total_payment = IBR_total_payment player.IBR_1_5_net = IBR_1_5_net player.IBR_6_10_net= IBR_6_10_net player.IBR_11_20_net= IBR_11_20_net player.IBR_20_net= IBR_20_net player.IBR_15_payment= IBR_15_payment player.IBR_610_payment= IBR_610_payment player.IBR_11_payment=IBR_11_payment player.SAVE_year=round(SAVE_year,1) player.save_forgiven=round(save_forgiven,1) player.save_total_payment=round(save_total_payment,1) player.save_1_5_net=round(save_1_5_net,1) player.save_6_10_net=round(save_6_10_net,1) player.save_11_20_net=round(save_11_20_net,1) player.save_20_net=round(save_20_net,1) player.save_15_payment=round(save_15_payment,1) player.save_610_payment=round(save_610_payment,1) player.save_11_payment = round(save_11_payment,1) # def is_displayed(self): # return self.player.choice_of_app == Constants.quadratic ########################################################################## # def vars_for_template(self): # if self.player.concern == Constants.job and self.player.education == Constants.economics: # # return { # 'a': 'please sign up for our upcoming job fair', # } # # elif (self.player.concern == Constants.health or self.player.concern == Constants.job) and (self.player.education == Constants.economics or self.player.education == Constants.other): # return { # 'a': 'we have a face mask for sale' # } ######################################################################### ################################################################################ # class Input_Investment(Page): # form_model = 'player' # form_fields = ['principal','interest_rate','maturity_period'] # # def is_displayed(self): # return self.player.choice_of_app==Constants.investment # # def vars_for_template(self): # if self.player.concern == Constants.job and self.player.education == Constants.economics: # # return { # 'a': 'please sign up for our upcoming job fair', # } # # elif (self.player.concern == Constants.health or self.player.concern == Constants.job) and (self.player.education == Constants.economics or self.player.education == Constants.other): # return { # 'a': 'we have a face mask for sale' # } ###################################################################################### ################################################################################### class Results_Quadratic(Page): pass class endnote(Page): pass ########################################################################## ############################################################################################################################################################ ########################################################################################################################################################## page_sequence = [Input_Quadratic,Results_Quadratic,endnote] # page_sequence = [ # Introduction, # Instruction, # Input_Quadratic, # Input_Investment, # Results_Quadratic, # Results_Investment, # Questionnaire_Covid_1, # Results_Questionnaire_Covid_1, # Questionnaire_Covid_2, # Results_Questionnaire_Covid_2, # Questionnaire_Covid_3, # Results_Questionnaire_Covid_3, # Questionnaire_Covid_4, # Results_Questionnaire_Covid_4, # Appreciation, # ]