From 32448ebed30a08d2568f9e06ae60befb484202b9 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 26 May 2017 13:10:59 -0400 Subject: [PATCH 01/17] Kick start budget simulator func development --- bpfin/financials/financial_budget_simulator.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 bpfin/financials/financial_budget_simulator.py diff --git a/bpfin/financials/financial_budget_simulator.py b/bpfin/financials/financial_budget_simulator.py new file mode 100644 index 0000000..6c8f6ee --- /dev/null +++ b/bpfin/financials/financial_budget_simulator.py @@ -0,0 +1,4 @@ +""" +bpfin.financials.financial_budget_simulator +run budget calculator and return table of results +""" -- GitLab From 4063c1e0b717170686e79ed535661389c0299f49 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 26 May 2017 17:30:42 -0400 Subject: [PATCH 02/17] Transfer old work code --- .../financials/financial_budget_simulator.py | 64 +++++++++++++++++++ bpfin/financials/loan_allocation.py | 63 ------------------ 2 files changed, 64 insertions(+), 63 deletions(-) diff --git a/bpfin/financials/financial_budget_simulator.py b/bpfin/financials/financial_budget_simulator.py index 6c8f6ee..91e70df 100644 --- a/bpfin/financials/financial_budget_simulator.py +++ b/bpfin/financials/financial_budget_simulator.py @@ -2,3 +2,67 @@ bpfin.financials.financial_budget_simulator run budget calculator and return table of results """ + +# ******** the following is draft for old work, for budge calculator development ******** + +# ## LP function, return total budget and variables +# def budget_LP(Financial_list,Loan_list,saving_percentage,other_loan,exp_pb,SF_fin,is_loan_first,saving_contigency,min_DSCR): +# # loan information ************* +# other_loan_DS=other_loan +# loan_max_list=[] +# ratio_list=[] +# DSmax_list=[] +# for loan in Loan_list: +# loan_max_list.append(loan.max_amount) +# ratio=cal_loan_payback(loan.interest,loan.duration) +# ratio_list.append(ratio) +# DSmax_list.append(loan.max_amount/ratio) +# DSmax=sum(DSmax_list) + +# # client preference ************* +# ratio_SF=exp_pb #expected payback period for self finance +# # is_loan_first = does client want to reduce loan by self-finance +# # willingtopay = max amount that the client can/want to self-finance + +# # financial and savings ************* +# noi=average([Financial_list[0].noi,Financial_list[1].noi,Financial_list[2].noi]) +# savings=average([Financial_list[0].energy_opex,Financial_list[1].energy_opex,Financial_list[2].energy_opex])*saving_percentage + +# ## object formula and conditions ********************************** +# c =[-1]*(len(Loan_list)+1) + +# a_base=[] +# for ratio in ratio_list: +# a_base.append(1/ratio) +# a1=[] +# a2=[] +# for pp in a_base: +# a1.append(pp) +# a2.append(pp) +# a1.append(1/ratio_SF) +# a2.append(0) +# a3=[0]*(len(Loan_list)+1) +# a3[-1]=1 + +# A =[ +# a1, +# a2, +# a3 +# ] +# b =[ +# savings/saving_contigency, +# ((noi+savings)/min_DSCR-other_loan_DS), +# SF_fin +# ] + +# bound_list=[] +# for loan_max in loan_max_list: +# bound_list.append((0,loan_max)) +# bound_list.append((0,(max(savings-DSmax,0)*ratio_SF if is_loan_first==True else None))) +# bounds=bound_list + +# res = linprog(c,A_ub=A,b_ub=b,bounds=bounds,options={'disp':False}) +# # print('Max_Budget',-res.fun) +# # print(res.x) +# # print('\n') +# return [-res.fun,res.x] diff --git a/bpfin/financials/loan_allocation.py b/bpfin/financials/loan_allocation.py index 5bcc9d1..4423c34 100644 --- a/bpfin/financials/loan_allocation.py +++ b/bpfin/financials/loan_allocation.py @@ -103,66 +103,3 @@ def loan_allocation( return res.x -# ******** the following is draft for old work, for budge calculator development ******** - -# ## LP function, return total budget and variables -# def budget_LP(Financial_list,Loan_list,saving_percentage,other_loan,exp_pb,SF_fin,is_loan_first,saving_contigency,min_DSCR): -# # loan information ************* -# other_loan_DS=other_loan -# loan_max_list=[] -# ratio_list=[] -# DSmax_list=[] -# for loan in Loan_list: -# loan_max_list.append(loan.max_amount) -# ratio=cal_loan_payback(loan.interest,loan.duration) -# ratio_list.append(ratio) -# DSmax_list.append(loan.max_amount/ratio) -# DSmax=sum(DSmax_list) - -# # client preference ************* -# ratio_SF=exp_pb #expected payback period for self finance -# # is_loan_first = does client want to reduce loan by self-finance -# # willingtopay = max amount that the client can/want to self-finance - -# # financial and savings ************* -# noi=average([Financial_list[0].noi,Financial_list[1].noi,Financial_list[2].noi]) -# savings=average([Financial_list[0].energy_opex,Financial_list[1].energy_opex,Financial_list[2].energy_opex])*saving_percentage - -# ## object formula and conditions ********************************** -# c =[-1]*(len(Loan_list)+1) - -# a_base=[] -# for ratio in ratio_list: -# a_base.append(1/ratio) -# a1=[] -# a2=[] -# for pp in a_base: -# a1.append(pp) -# a2.append(pp) -# a1.append(1/ratio_SF) -# a2.append(0) -# a3=[0]*(len(Loan_list)+1) -# a3[-1]=1 - -# A =[ -# a1, -# a2, -# a3 -# ] -# b =[ -# savings/saving_contigency, -# ((noi+savings)/min_DSCR-other_loan_DS), -# SF_fin -# ] - -# bound_list=[] -# for loan_max in loan_max_list: -# bound_list.append((0,loan_max)) -# bound_list.append((0,(max(savings-DSmax,0)*ratio_SF if is_loan_first==True else None))) -# bounds=bound_list - -# res = linprog(c,A_ub=A,b_ub=b,bounds=bounds,options={'disp':False}) -# # print('Max_Budget',-res.fun) -# # print(res.x) -# # print('\n') -# return [-res.fun,res.x] -- GitLab From 9c9e723749ad307627d2484ada0fd39f956dc97a Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Tue, 30 May 2017 18:28:32 -0400 Subject: [PATCH 03/17] Realize budget_simulatior --- .../financials/financial_budget_simulator.py | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) diff --git a/bpfin/financials/financial_budget_simulator.py b/bpfin/financials/financial_budget_simulator.py index 91e70df..78d05a3 100644 --- a/bpfin/financials/financial_budget_simulator.py +++ b/bpfin/financials/financial_budget_simulator.py @@ -2,6 +2,178 @@ bpfin.financials.financial_budget_simulator run budget calculator and return table of results """ +# from bpfin.financials.loan_allocation import loan_allocation +from scipy.optimize import linprog +import copy +from bpfin.tests.testdata import sample_data as db +from bpfin.financials.loan import Loan_List +from bpfin.lib.other import sumproduct + + +def cal_max_budget( + loan_list, + downpayment_max, + expected_payback, + req_dscr, + first_year_noi, + first_year_cash, + savings, + is_loan_first): + """ + Take inputs of available financing options(loans and self-finance), and other constrains, + determine the maximum budget for a project and its financing plan. + Run a linear programming (LP) to determine the result, which is how much to borrow from each lender + Math process is as following: + **** pre-request is: + # quoted_cost <= sum_loan_max_amount + downpayment_max + **** LP constrains are: + 1. objective function, determine maximum budget. + x_1 + x_2 + x_3 + ... + x_n = max + * x_i is loan amount, x_n is self-finance amount + 2. x variables constraints + sum(x_i / payback_i) <= saving/dscr + * sum of annual debt service and self-finance payback <= saving/dscr + sum(x_i / payback_i) - (x_n / payback_n) <= min (saving/dscr, noi/dscr, cash/dscr) + * sum of loan debt service <= financial constraints, making project financially feasibile + 3. x variables bounds + 0 <= x_i <= loan[i]_max_amount + 0 <= x_n <= downpayment_max + 4. One of the following optional constrains applies, for 1 out of the 4 different financing levels: + 4.1 loan only, no self-finance down payment: + downpayment_max = 0 + 4.2 use loan first, leftover saving can support some self-finance amount + downpayment_max = 0 + when x_i are determined: + x_n = min(saving/dscr - sum(x_i), downpayment_max) + 4.3 use self-finance first, leftover saving can support some loan amount + no additional constrain applied + 4.4 use all available financing, self-finance's expected payback is infinite + expected_payback = None + + Args: + loan_list (list): list of objectives of loans + customer_preference (dictionary): customer preference for financing plan + req_dscr(dictionary): required dscr + # cost (float): construction cost, can be estimated or quoted + first_year_saving (float): first year saving. # maybe it can be min_annual_saving + first_year_noi (float): first year noi, after commissioning date. # maybe it can be min_noi + first_year_cash (float): first year cash, after commissioning date + is_loan_first (boolean): 0 == client wants to reduce loan with self-finance, 1 == use as much loan as possible + # liability: do we need this? it's already calculated in balance sheet + # commissioning_date (date): construction finish date. Saving start at NEXT month + # scenario (dictionary): package of energy conservation measures (ECMs) + + Return: + float: max budget with given conditions + list: list of loan amount that borrowed from each lender, including self-finance + + Description: + req_dscr (dictionary): dict of required debt service coverage ratios, { + 'req_noi_dscr': 1.15, + 'req_cash_dscr': 1.15, + 'req_saving_dscr': 1.10 + } + Note: + noi == net operating income + dscr == debt service coverage ratio + + To do: calculate self_finance amount + if customer wants to self-finance, but loan first -> SF = cost - sum(xi) + if customer wants to S-F, but SF first -> max(xi) = cost - SF_max + """ + # sum_loan_max_amount = sum(list(current_loan.max_amount for current_loan in loan_list)) + # # pre-request judge: loan_list has a length + print(len(loan_list)) + + # linear programming (LP) + # Set LP constrains. + # objective function: x1 + x2 + x3 +...+ xn = maximum + c_formula = [-1] * (len(loan_list) + 1) + # x variables constraint, x1/payback1 + x2/payback2 +...+ xn/payback_n <= saving/dscr + # x variables constraint, debt services <= required_debt_service + A_matrix = [] + a_0 = list(1 / current_loan.payback for current_loan in loan_list) + a_1 = copy.deepcopy(a_0) + a_2 = copy.deepcopy(a_0) + a_1.append(1 / expected_payback * 12 if expected_payback else 0) + a_2.append(0) + A_matrix.append(a_1) + A_matrix.append(a_2) + b_list = [] + b_list.append( + savings / req_dscr['req_saving_dscr'] + ) + + b_list.append(min( + savings / req_dscr['req_saving_dscr'], + first_year_noi / req_dscr['req_noi_dscr'], + first_year_cash / req_dscr['req_cash_dscr'] + )) + # x variables bounds, 0 <= x[i] <= loan[i]_max_amount + bound_list = list((0, current_loan.max_amount) for current_loan in loan_list) + bound_list.append((0, downpayment_max if not is_loan_first else 0)) + print('\ndscr_allow', savings / req_dscr['req_saving_dscr'], first_year_noi / req_dscr['req_noi_dscr'], first_year_cash / req_dscr['req_cash_dscr']) + print('\nLP constraints =', A_matrix, b_list, bound_list) + # LP calculation. x[i] == loan amount from loan[i] + res = linprog(c_formula, A_ub=A_matrix, b_ub=b_list, bounds=bound_list, options={'disp': True}) + total_ds = sumproduct(res.x, a_2) + if is_loan_first: + print('\n saving allow =', (savings/req_dscr['req_saving_dscr'] - total_ds) * expected_payback / 12) + downpayment = min((savings/req_dscr['req_saving_dscr'] - total_ds) * expected_payback / 12, downpayment_max) + res.x[-1] = downpayment + if res.success is False: + return [0] * len(loan_list) + return res.x + + +# 4.1 loan only +print( + cal_max_budget( + loan_list=Loan_List(db.loan_input_list).loan_list, + downpayment_max=0, + expected_payback=120, + req_dscr=db.req_dscr, + first_year_noi=10000, + first_year_cash=10000, + savings=15000, + is_loan_first=False)) + +# 4.2 loan first +print( + cal_max_budget( + loan_list=Loan_List(db.loan_input_list).loan_list, + downpayment_max=90000, + expected_payback=120, + req_dscr=db.req_dscr, + first_year_noi=10000, + first_year_cash=10000, + savings=15000, + is_loan_first=True)) + + +# 4.3 +print( + cal_max_budget( + loan_list=Loan_List(db.loan_input_list).loan_list, + downpayment_max=90000, + expected_payback=120, + req_dscr=db.req_dscr, + first_year_noi=10000, + first_year_cash=10000, + savings=15000, + is_loan_first=False)) + +# 4.4 all funds +print( + cal_max_budget( + loan_list=Loan_List(db.loan_input_list).loan_list, + downpayment_max=90000, + expected_payback=None, + req_dscr=db.req_dscr, + first_year_noi=10000, + first_year_cash=10000, + savings=15000, + is_loan_first=False)) # ******** the following is draft for old work, for budge calculator development ******** -- GitLab From 361f6691b4578a3bff72c4b7eb6fc592ffd15e5a Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Wed, 31 May 2017 00:40:14 -0400 Subject: [PATCH 04/17] Create backend_call func for budget simulation --- .../financials/financial_budget_simulator.py | 148 ++- bpfin/financials/financial_income.py | 960 ++++++++------- bpfin/lib/back_end_call.py | 62 +- bpfin/lib/other.py | 3 + .../test_utilbills/test_back_end_call.py | 5 +- bpfin/tests/testdata/feature_data.py | 1073 ++++++++--------- 6 files changed, 1156 insertions(+), 1095 deletions(-) diff --git a/bpfin/financials/financial_budget_simulator.py b/bpfin/financials/financial_budget_simulator.py index 78d05a3..ba60bb9 100644 --- a/bpfin/financials/financial_budget_simulator.py +++ b/bpfin/financials/financial_budget_simulator.py @@ -2,12 +2,11 @@ bpfin.financials.financial_budget_simulator run budget calculator and return table of results """ -# from bpfin.financials.loan_allocation import loan_allocation -from scipy.optimize import linprog import copy +from scipy.optimize import linprog +from bpfin.lib.other import sumproduct from bpfin.tests.testdata import sample_data as db from bpfin.financials.loan import Loan_List -from bpfin.lib.other import sumproduct def cal_max_budget( @@ -83,7 +82,7 @@ def cal_max_budget( """ # sum_loan_max_amount = sum(list(current_loan.max_amount for current_loan in loan_list)) # # pre-request judge: loan_list has a length - print(len(loan_list)) + # print(len(loan_list)) # linear programming (LP) # Set LP constrains. @@ -103,7 +102,6 @@ def cal_max_budget( b_list.append( savings / req_dscr['req_saving_dscr'] ) - b_list.append(min( savings / req_dscr['req_saving_dscr'], first_year_noi / req_dscr['req_noi_dscr'], @@ -112,68 +110,102 @@ def cal_max_budget( # x variables bounds, 0 <= x[i] <= loan[i]_max_amount bound_list = list((0, current_loan.max_amount) for current_loan in loan_list) bound_list.append((0, downpayment_max if not is_loan_first else 0)) - print('\ndscr_allow', savings / req_dscr['req_saving_dscr'], first_year_noi / req_dscr['req_noi_dscr'], first_year_cash / req_dscr['req_cash_dscr']) - print('\nLP constraints =', A_matrix, b_list, bound_list) - # LP calculation. x[i] == loan amount from loan[i] - res = linprog(c_formula, A_ub=A_matrix, b_ub=b_list, bounds=bound_list, options={'disp': True}) + # print('\ndscr_allow', savings / req_dscr['req_saving_dscr'], first_year_noi / req_dscr['req_noi_dscr'], first_year_cash / req_dscr['req_cash_dscr']) + # print('\nLP constraints =', A_matrix, b_list, bound_list) + + # LP calculation. x[i] == loan amount from loan[i], x[-1] == self-finance amount + res = linprog(c_formula, A_ub=A_matrix, b_ub=b_list, bounds=bound_list, options={'disp': False}) total_ds = sumproduct(res.x, a_2) + if not res.success: + return [0] * (len(loan_list) + 1) if is_loan_first: - print('\n saving allow =', (savings/req_dscr['req_saving_dscr'] - total_ds) * expected_payback / 12) + # print('\n saving allow =', (savings/req_dscr['req_saving_dscr'] - total_ds) * expected_payback / 12) downpayment = min((savings/req_dscr['req_saving_dscr'] - total_ds) * expected_payback / 12, downpayment_max) res.x[-1] = downpayment - if res.success is False: - return [0] * len(loan_list) return res.x -# 4.1 loan only -print( - cal_max_budget( - loan_list=Loan_List(db.loan_input_list).loan_list, +def form_budget( + loan_list, + downpayment_max, + expected_payback, + req_dscr, + first_year_noi, + first_year_cash, + savings): + """ + + Return: + dictionary: dict of tuples. each tuple represents a financing plan, with and budget, financing plan + Description: + budget = { + 'loan_only': (max_budget, [x[1], x[2], x[3], ..., x[n]]), + 'loan_first': (max_budget, [x[1], x[2], x[3], ..., x[n]]), + 'sf_first': (), + 'sf_max': () + } + """ + budget = {} + result = cal_max_budget( + loan_list=loan_list, downpayment_max=0, - expected_payback=120, - req_dscr=db.req_dscr, - first_year_noi=10000, - first_year_cash=10000, - savings=15000, - is_loan_first=False)) - -# 4.2 loan first -print( - cal_max_budget( - loan_list=Loan_List(db.loan_input_list).loan_list, - downpayment_max=90000, - expected_payback=120, - req_dscr=db.req_dscr, - first_year_noi=10000, - first_year_cash=10000, - savings=15000, - is_loan_first=True)) - - -# 4.3 -print( - cal_max_budget( - loan_list=Loan_List(db.loan_input_list).loan_list, - downpayment_max=90000, - expected_payback=120, - req_dscr=db.req_dscr, - first_year_noi=10000, - first_year_cash=10000, - savings=15000, - is_loan_first=False)) - -# 4.4 all funds -print( - cal_max_budget( - loan_list=Loan_List(db.loan_input_list).loan_list, - downpayment_max=90000, + expected_payback=expected_payback, + req_dscr=req_dscr, + first_year_noi=first_year_noi, + first_year_cash=first_year_cash, + savings=savings, + is_loan_first=False + ) + budget['loan_only'] = (sum(result), list(result)) + + result = cal_max_budget( + loan_list=loan_list, + downpayment_max=downpayment_max, + expected_payback=expected_payback, + req_dscr=req_dscr, + first_year_noi=first_year_noi, + first_year_cash=first_year_cash, + savings=savings, + is_loan_first=True + ) + budget['loan_first'] = (sum(result), list(result)) + + result = cal_max_budget( + loan_list=loan_list, + downpayment_max=downpayment_max, + expected_payback=expected_payback, + req_dscr=req_dscr, + first_year_noi=first_year_noi, + first_year_cash=first_year_cash, + savings=savings, + is_loan_first=False + ) + budget['sf_first'] = (sum(result), list(result)) + + result = cal_max_budget( + loan_list=loan_list, + downpayment_max=downpayment_max, expected_payback=None, - req_dscr=db.req_dscr, - first_year_noi=10000, - first_year_cash=10000, - savings=15000, - is_loan_first=False)) + req_dscr=req_dscr, + first_year_noi=first_year_noi, + first_year_cash=first_year_cash, + savings=savings, + is_loan_first=False + ) + budget['sf_max'] = (sum(result), list(result)) + + return budget + + +print(form_budget( + loan_list=Loan_List(db.loan_input_list).loan_list, + downpayment_max=90000, + expected_payback=120, + req_dscr=db.req_dscr, + first_year_noi=10000, + first_year_cash=10000, + savings=15000)) + # ******** the following is draft for old work, for budge calculator development ******** diff --git a/bpfin/financials/financial_income.py b/bpfin/financials/financial_income.py index 10781b4..d42a4ca 100644 --- a/bpfin/financials/financial_income.py +++ b/bpfin/financials/financial_income.py @@ -1,465 +1,495 @@ -# """ -# Rewrite Income_Statement class to saperate income statement related class and funcs from Financial_lib.py -# Income_Statement is incorporate with new Bill class, taking in bill_list as input -# """ - -# import copy -# from bpfin.lib.other import cal_cagr -# from bpfin.utilbills.bill_lib import form_bill_year -# from numpy import mean -# # from bpfin.tests.testdata import sample_data as db -# # import pprint -# from bpfin.lib.other import average_list_with_none - - -# class Income_Statement(): -# """ -# Generate Income Statement for single year, with standard accounting line items -# Will be called -# """ -# def __init__(self): -# self.year = None -# self.revenue = None -# self.utility_expense = None -# self.non_utility_expense = None -# self.electricity_opex = None -# self.gas_opex = None -# self.oil_opex = None -# self.water_opex = None -# self.energy_opex = None -# self.other_utility = None -# self.net_non_energy_opex = None -# self.total_opex = None -# self.noi = None -# # energy_debt_service = -# # other_debt_service = -# # total_debt_service = -# # bill_saving = -# # energy_DSCR = -# # total_DSCR = - -# def put_hist(self, year, income_input, annual_bill_table): -# """ -# Put historical income statement data and generate a single year income statement -# Inputs are incomplete income statement items, and energy bill overview -# Final output is an single year income_statement objectvie with standarized items filled - -# Args: -# year (int): the year of this income statement -# income_input (dictionary): 3 elements of inputs -# annual_bill_table (dictionary): annual bill, for 4 utility_types - -# Description Sample: -# income_input = {revenue': 100000, 'utility_expense': 60000,'non_utility_expense': 3000} -# annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} -# """ -# self.year = year -# self.revenue = income_input['revenue'] -# self.utility_expense = income_input['utility_expense'] -# self.non_utility_expense = income_input['non_utility_expense'] -# self.electricity_opex = annual_bill_table['electricity'][self.year] -# self.gas_opex = annual_bill_table['gas'][self.year] -# self.oil_opex = annual_bill_table['oil'][self.year] -# self.water_opex = annual_bill_table['water'][self.year] -# self.energy_opex = self.electricity_opex + self.oil_opex + self.gas_opex + self.water_opex -# self.other_utility = self.utility_expense - self.energy_opex -# self.net_non_energy_opex = self.other_utility + self.non_utility_expense -# self.total_opex = self.energy_opex + self.net_non_energy_opex -# self.noi = self.revenue - self.total_opex # net operating income - -# def put_average(self, average_revenue, annual_bills, characters): -# """ -# Calculate and create average income statement. -# Revenue, energy bills are average of historical data. -# Other items are calculated by historical characters. -# Args: -# average_revenue (float): average revenue calculated from historical income statements -# annual_bills (dictionary): dictionary of float value of average annual bills, for 4 utility_types -# characters (dictionary): 6 characters calculated from historical income statements -# Final instance is a single year income statement without named year - -# Description: -# characters = { -# 'start_year': (int) 2014, -# 'end_year': (int) 2016, -# 'cagr': (float) 0.054, -# 'other_utility_percent': (float) 0.020 == 2.0% -# 'non_utility_expense_percent': (float) 0.030 == 3.0% -# 'revenue_average': (float) 3000.00 -# } -# annual_bills = {'electricity': 100.0, 'oil': 200.0, 'gas': 300.0, 'water': 400.0} -# """ -# self.revenue = average_revenue -# self.electricity_opex = annual_bills['electricity'] -# self.gas_opex = annual_bills['gas'] -# self.oil_opex = annual_bills['oil'] -# self.water_opex = annual_bills['water'] -# self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex -# self.other_utility = self.revenue * characters['other_utility_percent'] -# self.non_utility_expense = self.revenue * characters[ -# 'non_utility_expense_percent'] -# self.utility_expense = self.energy_opex + self.other_utility -# self.net_non_energy_opex = self.other_utility + self.non_utility_expense -# self.total_opex = self.energy_opex + self.net_non_energy_opex -# self.noi = self.revenue - self.total_opex - -# # def assign_next(self, income_statement_characters, -# # bill_overview_organized): -# # """ -# # !!! this function will be deleted in the next version!! -# # """ -# # self.electricity_opex = bill_overview_organized['electricity'][ -# # self.year] -# # self.gas_opex = bill_overview_organized['gas'][self.year] -# # self.oil_opex = bill_overview_organized['oil'][self.year] -# # self.water_opex = bill_overview_organized['water'][self.year] -# # self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex -# # self.other_utility = self.revenue * income_statement_characters[ -# # 'other_utility_percent'] -# # self.non_utility_expense = self.revenue * income_statement_characters[ -# # 'non_utility_expense_percent'] -# # self.utility_expense = self.energy_opex + self.other_utility -# # self.net_non_energy_opex = self.other_utility + self.non_utility_expense -# # self.total_opex = self.energy_opex + self.net_non_energy_opex -# # self.noi = self.revenue - self.total_opex - - -# def convert_income_statement_class(income_statement_class): -# """ -# Convert single year income statement object into a dictionary format -# Args: -# income_statement_class (object): single year income statement object -# Return: -# income_statement_dict (dictionary) -# """ -# income_statement_dict = { -# 'year': income_statement_class.year, -# 'revenue': income_statement_class.revenue, -# 'utility_expense': income_statement_class.utility_expense, -# 'energy_opex': income_statement_class.energy_opex, -# 'electricity_opex': income_statement_class.electricity_opex, -# 'gas_opex': income_statement_class.gas_opex, -# 'oil_opex': income_statement_class.oil_opex, -# 'water_opex': income_statement_class.water_opex, -# 'other_utility': income_statement_class.other_utility, -# 'non_utility_expense': income_statement_class.non_utility_expense, -# 'net_non_energy_opex': income_statement_class.net_non_energy_opex, -# 'total_opex': income_statement_class.total_opex, -# 'noi': income_statement_class.noi -# } -# return income_statement_dict - - -# class Income_Statement_Next(): -# """ -# Create single year income_statement object, -# with input of last year data, income statement characters, and projected annual bill -# """ - -# def __init__(self, year, last_revenue, growth_rate_flag, characters, -# annual_bill_table): -# """ -# Calculation is done in initiation. -# Args: -# year (int): the year that will be projected on -# last_revenue (float): last year revenue -# growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average -# characters (dictionary): 6 characters calculated from historical income statements -# annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types -# Final instance is a single year income_statement object - -# Description: -# characters = { -# 'start_year': (int) 2014, -# 'end_year': (int) 2016, -# 'cagr': (float) 0.054, -# 'other_utility_percent': (float) 0.020 == 2.0% -# 'non_utility_expense_percent': (float) 0.035 == 3.5% -# 'revenue_average': (float) 3000.00 -# } -# annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} -# electricity_bill = {2014: 100, 2015:200, ...} -# """ -# self.year = year - -# if growth_rate_flag == -1.0: # growth_rate_flag == average -# current_revenue = characters['revenue_average'] -# else: -# if growth_rate_flag == -2.0: # growth_rate_flag == cagr -# growth_rate = characters['cagr'] -# else: -# growth_rate = growth_rate_flag # growth_rate_flag == 0.00, 0.01, ... -# current_revenue = last_revenue * (1 + growth_rate) -# self.revenue = current_revenue -# self.electricity_opex = annual_bill_table['electricity'][self.year] -# self.gas_opex = annual_bill_table['gas'][self.year] -# self.oil_opex = annual_bill_table['oil'][self.year] -# self.water_opex = annual_bill_table['water'][self.year] -# self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex -# self.other_utility = self.revenue * characters['other_utility_percent'] -# self.non_utility_expense = self.revenue * characters[ -# 'non_utility_expense_percent'] -# self.utility_expense = self.energy_opex + self.other_utility -# self.net_non_energy_opex = self.other_utility + self.non_utility_expense -# self.total_opex = self.energy_opex + self.net_non_energy_opex -# self.noi = self.revenue - self.total_opex - - -# class Income_Statement_Table(): -# """ -# Create a income statement table, containing multiple years, including historical and projected data -# Key Attributes are: -# hist_table (list): list of single year income_statement objects, containing historical data -# table(list): list of single year income_statement objects, containing historical and projected data -# """ - -# def __init__(self, raw_income_input, annual_bill_table): -# """ -# Create hist_table to store historical income statement data, and calculate some key characters. -# Args: -# raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year -# annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types - -# Key Attributes: -# cagr (float): compound annual growth rate -# other_utility_percent (float): percentage, (average other_utility / average revenue) -# non_utility_expense_percent (float): percentage, (average non_utility_expense_percent / average revenue) -# hist_table (list): list of single year income_statement objects, containing historical data -# table(list): list of single year income_statement objects, containing historical and projected data -# characters (dictionary): contains key characters determined from historical data. Is used to project - -# Description: -# raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} -# annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} -# electricity_bill = {2014: 100, 2015:200, ...} -# characters = { -# 'start_year': (int) 2014, -# 'end_year': (int) 2016, -# 'cagr': (float) 0.054, -# 'other_utility_percent': (float) 0.02, -# 'non_utility_expense_percent': (float) 0.03, -# 'revenue_average': (float) 3000.00 -# } -# """ -# self.hist_start_year = None -# self.hist_end_year = None -# self.cagr = 0.00 -# self.other_utility_percent = 0.00 -# self.non_utility_expense_percent = 0.00 -# self.revenue_average = 0.00 # average revenue -# self.hist_table = [] -# self.table = [] -# self.characters = {} - -# for year in raw_income_input: -# current_income_statement = Income_Statement() -# current_income_statement.put_hist(year, raw_income_input[year], -# annual_bill_table) -# self.hist_table.append(current_income_statement) - -# sorted_income_hist_year = sorted(raw_income_input) -# self.hist_start_year = sorted_income_hist_year[0] -# self.hist_end_year = sorted_income_hist_year[-1] -# # if start_year == None: return None -# self.cagr = cal_cagr( -# raw_income_input[self.hist_start_year]['revenue'], -# raw_income_input[self.hist_end_year]['revenue'], -# self.hist_end_year - self.hist_start_year) - -# revenue_sum = 0 -# other_utility_sum = 0 -# non_utility_expense_sum = 0 -# for current_income_statement in self.hist_table: -# revenue_sum += current_income_statement.revenue -# other_utility_sum += current_income_statement.other_utility -# non_utility_expense_sum += current_income_statement.non_utility_expense - -# self.other_utility_percent = other_utility_sum / revenue_sum -# self.non_utility_expense_percent = non_utility_expense_sum / revenue_sum -# self.revenue_average = revenue_sum / ( -# self.hist_end_year - self.hist_start_year + 1) - -# self.table = self.hist_table -# self.characters = { -# 'start_year': self.hist_start_year, -# 'end_year': self.hist_end_year, -# 'cagr': self.cagr, -# 'other_utility_percent': self.other_utility_percent, -# 'non_utility_expense_percent': self.non_utility_expense_percent, -# 'revenue_average': self.revenue_average -# } - -# def project(self, growth_rate_flag, analysis_date, annual_bill_table): -# """ -# Project future income statement. Append multiple single year income_statement objects to self.table -# Args: -# growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average -# analysis_date (dictionary): proforma's starting date and the years of proforma -# annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types -# Return: -# list: list of single year income_statement objects, containing historical and projection -# Note: -# project() overwrites existing projection data -# """ -# # characters = copy.deepcopy(self.characters) -# proforma_year = form_bill_year(analysis_date['proforma_start'], -# analysis_date['proforma_duration']) -# current_table = copy.deepcopy(self.hist_table) -# for year in proforma_year: -# last_revenue = current_table[-1].revenue -# if year <= current_table[-1].year: -# continue -# current_income_statement = Income_Statement_Next( -# year, last_revenue, growth_rate_flag, self.characters, -# annual_bill_table) -# current_table.append(current_income_statement) -# self.table = current_table -# # return current_table - -# def get_hist_table(self): -# """ -# Get historical table, in dictionary formatting -# Return: -# dictionary: dict of dict of income statement. Key is year -# Description: -# hist_table_dict = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} -# """ -# hist_table_dict = {} -# for current_income_statement in self.hist_table: -# hist_table_dict[current_income_statement.year] = { -# 'year': -# current_income_statement.year, -# 'revenue': -# current_income_statement.revenue, -# 'utility_expense': -# current_income_statement.utility_expense, -# 'energy_opex': -# current_income_statement.energy_opex, -# 'electricity_opex': -# current_income_statement.electricity_opex, -# 'gas_opex': -# current_income_statement.gas_opex, -# 'oil_opex': -# current_income_statement.oil_opex, -# 'water_opex': -# current_income_statement.water_opex, -# 'other_utility': -# current_income_statement.other_utility, -# 'non_utility_expense': -# current_income_statement.non_utility_expense, -# 'net_non_energy_opex': -# current_income_statement.net_non_energy_opex, -# 'total_opex': -# current_income_statement.total_opex, -# 'noi': -# current_income_statement.noi -# } -# return hist_table_dict - -# def get_cagr(self): -# """ -# Get compound annual growth rate -# Return: -# float: compound annual growth rate -# """ -# return copy.deepcopy(self.cagr) - -# def get_average(self): -# """ -# Get average income statement. Only need initialized Income_Statement_Table -# Return: -# dictionary: dict of average income statement, Keys are income statement items. -# Description: -# output_dict = {'year': None, 'revenue': 95000.0, ... ,'noi': 35166.666666666657} -# """ -# # current_year = 'Average' -# current_income_statement = Income_Statement() -# average_revenue = mean(list(i_s.revenue for i_s in self.hist_table)) -# annual_bills = { -# 'electricity': -# mean(list(i_s.electricity_opex for i_s in self.hist_table)), -# 'gas': -# mean(list(i_s.gas_opex for i_s in self.hist_table)), -# 'oil': -# mean(list(i_s.oil_opex for i_s in self.hist_table)), -# 'water': -# mean(list(i_s.water_opex for i_s in self.hist_table)) -# } -# current_income_statement.put_average(average_revenue, annual_bills, -# self.characters) - -# return convert_income_statement_class(current_income_statement) - -# def get_single_year(self, year): -# """ -# Get single year income statement from self.table. Table can either contain or not contain projection -# Args: -# year (int): the year that need to be extracted -# Return: -# dict: single year income statement. Keys are income statement items. -# Note: -# if the target year is not in self.table, then return None. -# """ -# for current_income_statement in self.table: -# if current_income_statement.year == year: -# return { -# 'year': -# current_income_statement.year, -# 'revenue': -# current_income_statement.revenue, -# 'utility_expense': -# current_income_statement.utility_expense, -# 'energy_opex': -# current_income_statement.energy_opex, -# 'electricity_opex': -# current_income_statement.electricity_opex, -# 'gas_opex': -# current_income_statement.gas_opex, -# 'oil_opex': -# current_income_statement.oil_opex, -# 'water_opex': -# current_income_statement.water_opex, -# 'other_utility': -# current_income_statement.other_utility, -# 'non_utility_expense': -# current_income_statement.non_utility_expense, -# 'net_non_energy_opex': -# current_income_statement.net_non_energy_opex, -# 'total_opex': -# current_income_statement.total_opex, -# 'noi': -# current_income_statement.noi -# } -# return None - -# def get_noi_dict(self): -# """ -# Get a dictionary of net operating income. -# Return: -# dictionary: net operating income for each year in income statement table. Key is year -# """ -# noi_dict = {} -# for current_income_statement in self.table: -# noi_dict[ -# current_income_statement.year] = current_income_statement.noi -# return noi_dict - -# def get_first_year_noi(self, commission_date): -# """ -# Get first year noi after commissioning year, from post_saving income statement -# Args: -# commission_date (date): construction finishing date. Saving starts NEXT month/year -# Return: -# float: noi of the next year after commission date -# """ -# first_year = commission_date.year + 1 -# for current_income_statement in self.table: -# if current_income_statement.year == first_year: -# return current_income_statement.noi - -# def get_total_energy_dict(self): -# total_energy_dict = {} -# for current_income_statement in self.table: -# total_energy_dict[ -# current_income_statement.year] = current_income_statement.energy_opex -# return total_energy_dict +""" +Rewrite Income_Statement class to saperate income statement related class and funcs from Financial_lib.py +Income_Statement is incorporate with new Bill class, taking in bill_list as input +""" +import copy +from bpfin.lib.other import cal_cagr +# from bpfin.lib import other as lib +from bpfin.utilbills.bill_lib import form_bill_year +from numpy import mean +from bpfin.lib.other import UTILITY_TYPE_LIST + + +class Income_Statement(): + def __init__(self): + self.year = None + self.revenue = None + self.utility_expense = None + self.non_utility_expense = None + self.electricity_opex = None + self.gas_opex = None + self.oil_opex = None + self.water_opex = None + self.energy_opex = None + self.other_utility = None + self.net_non_energy_opex = None + self.total_opex = None + self.noi = None + # energy_debt_service = + # other_debt_service = + # total_debt_service = + # bill_saving = + # energy_DSCR = + # total_DSCR = + + def put_hist(self, year, income_input, annual_bill_table): + """ + Put historical income statement data and generate a single year income statement + Inputs are incomplete income statement items, and energy bill overview + Final output is an single year income_statement objectvie with standarized items filled + Args: + year (int): the year of this income statement + income_input (dictionary): 3 elements of inputs + annual_bill_table (dictionary): annual bill, for 4 utility_types + Description Sample: + income_input = {revenue': 100000, 'utility_expense': 60000,'non_utility_expense': 3000} + annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} + """ + self.year = year + self.revenue = income_input['revenue'] + self.utility_expense = income_input['utility_expense'] + self.non_utility_expense = income_input['non_utility_expense'] + self.electricity_opex = annual_bill_table['electricity'][self.year] + self.gas_opex = annual_bill_table['gas'][self.year] + self.oil_opex = annual_bill_table['oil'][self.year] + self.water_opex = annual_bill_table['water'][self.year] + self.energy_opex = self.electricity_opex + self.oil_opex + self.gas_opex + self.water_opex + self.other_utility = self.utility_expense - self.energy_opex + self.net_non_energy_opex = self.other_utility + self.non_utility_expense + self.total_opex = self.energy_opex + self.net_non_energy_opex + self.noi = self.revenue - self.total_opex # net operating income + + def put_average(self, average_revenue, annual_bills, characters): + """ + Calculate and create average income statement. + Revenue, energy bills are average of historical data. + Other items are calculated by historical characters. + Args: + average_revenue (float): average revenue calculated from historical income statements + annual_bills (dictionary): dictionary of float value of average annual bills, for 4 utility_types + characters (dictionary): 6 characters calculated from historical income statements + Final instance is a single year income statement without named year + Description: + characters = { + 'start_year': (int) 2014, + 'end_year': (int) 2016, + 'cagr': (float) 0.054, + 'other_utility_percent': (float) 0.020 == 2.0% + 'non_utility_expense_percent': (float) 0.030 == 3.0% + 'revenue_average': (float) 3000.00 + } + annual_bills = {'electricity': 100.0, 'oil': 200.0, 'gas': 300.0, 'water': 400.0} + """ + self.revenue = average_revenue + self.electricity_opex = annual_bills['electricity'] + self.gas_opex = annual_bills['gas'] + self.oil_opex = annual_bills['oil'] + self.water_opex = annual_bills['water'] + self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex + self.other_utility = self.revenue * characters['other_utility_percent'] + self.non_utility_expense = self.revenue * characters[ + 'non_utility_expense_percent'] + self.utility_expense = self.energy_opex + self.other_utility + self.net_non_energy_opex = self.other_utility + self.non_utility_expense + self.total_opex = self.energy_opex + self.net_non_energy_opex + self.noi = self.revenue - self.total_opex + + +def convert_income_statement_class(income_statement_class): + """ + Convert single year income statement object into a dictionary format + Args: + income_statement_class (object): single year income statement object + Return: + income_statement_dict (dictionary) + """ + income_statement_dict = { + 'year': income_statement_class.year, + 'revenue': income_statement_class.revenue, + 'utility_expense': income_statement_class.utility_expense, + 'energy_opex': income_statement_class.energy_opex, + 'electricity_opex': income_statement_class.electricity_opex, + 'gas_opex': income_statement_class.gas_opex, + 'oil_opex': income_statement_class.oil_opex, + 'water_opex': income_statement_class.water_opex, + 'other_utility': income_statement_class.other_utility, + 'non_utility_expense': income_statement_class.non_utility_expense, + 'net_non_energy_opex': income_statement_class.net_non_energy_opex, + 'total_opex': income_statement_class.total_opex, + 'noi': income_statement_class.noi + } + return income_statement_dict + + +class Income_Statement_Next(): + """ + Create single year income_statement object, + with input of last year data, income statement characters, and projected annual bill + """ + + def __init__(self, year, last_revenue, growth_rate_flag, characters, annual_bill_table): + """ + Calculation is done in initiation. + Args: + year (int): the year that will be projected on + last_revenue (float): last year revenue + growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average + characters (dictionary): 6 characters calculated from historical income statements + annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + Final instance is a single year income_statement object + Description: + characters = { + 'start_year': (int) 2014, + 'end_year': (int) 2016, + 'cagr': (float) 0.054, + 'other_utility_percent': (float) 0.020 == 2.0% + 'non_utility_expense_percent': (float) 0.035 == 3.5% + 'revenue_average': (float) 3000.00 + } + annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} + electricity_bill = {2014: 100, 2015:200, ...} + """ + if not validate_growth_rate_flag(growth_rate_flag): + raise ValueError('growth_rate input is not valid') + self.year = year + if growth_rate_flag == -1.0: # growth_rate_flag == average + current_revenue = characters['revenue_average'] + else: + if growth_rate_flag == -2.0: # growth_rate_flag == cagr + growth_rate = characters['cagr'] + else: + growth_rate = growth_rate_flag # growth_rate_flag == 0.00, 0.01, ... + current_revenue = last_revenue * (1 + growth_rate) + self.revenue = current_revenue + self.electricity_opex = annual_bill_table['electricity'][self.year] + self.gas_opex = annual_bill_table['gas'][self.year] + self.oil_opex = annual_bill_table['oil'][self.year] + self.water_opex = annual_bill_table['water'][self.year] + self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex + self.other_utility = self.revenue * characters['other_utility_percent'] + self.non_utility_expense = self.revenue * characters['non_utility_expense_percent'] + self.utility_expense = self.energy_opex + self.other_utility + self.net_non_energy_opex = self.other_utility + self.non_utility_expense + self.total_opex = self.energy_opex + self.net_non_energy_opex + self.noi = self.revenue - self.total_opex + + +class Income_Statement_Table(): + """ + Create a income statement table, containing multiple years, including historical and projected data + Key Attributes are: + hist_table (list): list of single year income_statement objects, containing historical data + table(list): list of single year income_statement objects, containing historical and projected data + """ + + def __init__(self, raw_income_input, annual_bill_table, analysis_date): + """ + Create hist_table to store historical income statement data, and calculate some key characters. + Args: + raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year + annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + analysis_date (dictionary): proforma's starting date and the years of proforma + Key Attributes: + cagr (float): compound annual growth rate + other_utility_percent (float): percentage, (average other_utility / average revenue) + non_utility_expense_percent (float): percentage, (average non_utility_expense_percent / average revenue) + hist_table (list): list of single year income_statement objects, containing historical data + table(list): list of single year income_statement objects, containing historical and projected data + characters (dictionary): contains key characters determined from historical data. Is used to project + Description: + raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} + annual_bill_table = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} + electricity_bill = {2014: 100, 2015:200, ...} + characters = { + 'start_year': (int) 2014, + 'end_year': (int) 2016, + 'cagr': (float) 0.054, + 'other_utility_percent': (float) 0.02, + 'non_utility_expense_percent': (float) 0.03, + 'revenue_average': (float) 3000.00 + } + Error Validation: + raw_income_input: + year should not be empty. + year should be covered by analysis date. Raise: change analysis date + any year should contain 4 raw inputs. + annual_bill_table: + annual_bill_table years should cover proforma years + """ + if not validate_empty(raw_income_input): + raise ValueError('Income_Statement_Table-init() raw_income_input is empty') + if not validate_empty(analysis_date): + raise ValueError('Income_Statement_Table-init() analysis_date is empty') + # validate annual_bill_table + if not validate_annual_bill_table(annual_bill_table): + raise ValueError('Income_Statement_Table-init() annual_bill_table is invalid') + # validate raw_income_input + + self.hist_start_year = None + self.hist_end_year = None + self.cagr = 0.00 + self.other_utility_percent = 0.00 + self.non_utility_expense_percent = 0.00 + self.revenue_average = 0.00 # average revenue + self.hist_table = [] + self.table = [] + self.characters = {} + self.analysis_date = analysis_date + + for year in raw_income_input.keys(): + current_income_statement = Income_Statement() + current_income_statement.put_hist(year, raw_income_input[year], annual_bill_table) + self.hist_table.append(current_income_statement) + + sorted_income_hist_year = sorted(raw_income_input) + self.hist_start_year = sorted_income_hist_year[0] + self.hist_end_year = sorted_income_hist_year[-1] + self.cagr = cal_cagr( + raw_income_input[self.hist_start_year]['revenue'], + raw_income_input[self.hist_end_year]['revenue'], + self.hist_end_year - self.hist_start_year) + + revenue_sum = 0 + other_utility_sum = 0 + non_utility_expense_sum = 0 + for current_income_statement in self.hist_table: + revenue_sum += current_income_statement.revenue + other_utility_sum += current_income_statement.other_utility + non_utility_expense_sum += current_income_statement.non_utility_expense + + self.other_utility_percent = other_utility_sum / revenue_sum + self.non_utility_expense_percent = non_utility_expense_sum / revenue_sum + self.revenue_average = revenue_sum / (self.hist_end_year - self.hist_start_year + 1) + + self.table = self.hist_table + self.characters = { + 'start_year': self.hist_start_year, + 'end_year': self.hist_end_year, + 'cagr': self.cagr, + 'other_utility_percent': self.other_utility_percent, + 'non_utility_expense_percent': self.non_utility_expense_percent, + 'revenue_average': self.revenue_average + } + + def project(self, growth_rate_flag, annual_bill_table): + """ + Project future income statement. Append multiple single year income_statement objects to self.table + Args: + growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average + annual_bill_table (dictionary): dictionary of dictionary of annual bills, for 4 utility_types + Return: + list: list of single year income_statement objects, containing historical and projection + Note: + project() overwrites existing projection data + """ + if not validate_growth_rate_flag(growth_rate_flag): + raise ValueError('growth_rate input is not valid') + proforma_year = form_bill_year(self.analysis_date['proforma_start'], + self.analysis_date['proforma_duration']) + current_table = copy.deepcopy(self.hist_table) + for year in proforma_year: + last_revenue = current_table[-1].revenue + if year <= current_table[-1].year: + continue + current_income_statement = Income_Statement_Next( + year, last_revenue, growth_rate_flag, self.characters, + annual_bill_table) + current_table.append(current_income_statement) + self.table = current_table + # return current_table + + def get_hist_table(self): + """ + Get historical table, in dictionary formatting + Return: + dictionary: dict of dict of income statement. Key is year + Description: + hist_table_dict = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} + """ + hist_table_dict = {} + for current_income_statement in self.hist_table: + hist_table_dict[current_income_statement.year] = { + 'year': current_income_statement.year, + 'revenue': current_income_statement.revenue, + 'utility_expense': current_income_statement.utility_expense, + 'energy_opex': current_income_statement.energy_opex, + 'electricity_opex': current_income_statement.electricity_opex, + 'gas_opex': current_income_statement.gas_opex, + 'oil_opex': current_income_statement.oil_opex, + 'water_opex': current_income_statement.water_opex, + 'other_utility': current_income_statement.other_utility, + 'non_utility_expense': current_income_statement.non_utility_expense, + 'net_non_energy_opex': current_income_statement.net_non_energy_opex, + 'total_opex': current_income_statement.total_opex, + 'noi': current_income_statement.noi + } + return hist_table_dict + + def get_cagr(self): + """ + Get compound annual growth rate + Return: + float: compound annual growth rate + """ + return copy.deepcopy(self.cagr) + + def get_average(self): + """ + Get average income statement. Only need initialized Income_Statement_Table + Return: + dictionary: dict of average income statement, Keys are income statement items. + Description: + output_dict = {'year': None, 'revenue': 95000.0, ... ,'noi': 35166.666666666657} + """ + # current_year = 'Average' + current_income_statement = Income_Statement() + average_revenue = mean(list(i_s.revenue for i_s in self.hist_table)) + annual_bills = { + 'electricity': + mean(list(i_s.electricity_opex for i_s in self.hist_table)), + 'gas': + mean(list(i_s.gas_opex for i_s in self.hist_table)), + 'oil': + mean(list(i_s.oil_opex for i_s in self.hist_table)), + 'water': + mean(list(i_s.water_opex for i_s in self.hist_table)) + } + current_income_statement.put_average(average_revenue, annual_bills, self.characters) + return convert_income_statement_class(current_income_statement) + + def get_single_year(self, year): + """ + Get single year income statement from self.table. Table can either contain or not contain projection + Args: + year (int): the year that need to be extracted + Return: + dict: single year income statement. Keys are income statement items. + Note: + if the target year is not in self.table, then return None. + """ + for current_income_statement in self.table: + if current_income_statement.year == year: + return { + 'year': current_income_statement.year, + 'revenue': current_income_statement.revenue, + 'utility_expense': current_income_statement.utility_expense, + 'energy_opex': current_income_statement.energy_opex, + 'electricity_opex': current_income_statement.electricity_opex, + 'gas_opex': current_income_statement.gas_opex, + 'oil_opex': current_income_statement.oil_opex, + 'water_opex': current_income_statement.water_opex, + 'other_utility': current_income_statement.other_utility, + 'non_utility_expense': current_income_statement.non_utility_expense, + 'net_non_energy_opex': current_income_statement.net_non_energy_opex, + 'total_opex': current_income_statement.total_opex, + 'noi': current_income_statement.noi + } + return None + + def get_noi_dict(self): + """ + Get a dictionary of net operating income. + Return: + dictionary: net operating income for each year in income statement table. Key is year + """ + noi_dict = {} + for current_income_statement in self.table: + noi_dict[ + current_income_statement.year] = current_income_statement.noi + return noi_dict + + def get_first_year_noi(self, commission_date): + """ + Get first year noi after commissioning year, from post_saving income statement + Args: + commission_date (date): construction finishing date. Saving starts NEXT month/year + Return: + float: noi of the next year after commission date + """ + first_year = commission_date.year + 1 + for current_income_statement in self.table: + if current_income_statement.year == first_year: + return current_income_statement.noi + + def get_full_income_table(self): + """ + Get full projected income statement table + Return: + dictionary: dict of dict of income statement. Key is year + """ + # validate projection availability + if self.table[-1].year <= self.hist_end_year: + raise ValueError('income statement is not projected properly') + table_dict = {} + for current_income_statement in self.table: + table_dict[current_income_statement.year] = { + 'year': current_income_statement.year, + 'revenue': current_income_statement.revenue, + 'utility_expense': current_income_statement.utility_expense, + 'energy_opex': current_income_statement.energy_opex, + 'electricity_opex': current_income_statement.electricity_opex, + 'gas_opex': current_income_statement.gas_opex, + 'oil_opex': current_income_statement.oil_opex, + 'water_opex': current_income_statement.water_opex, + 'other_utility': current_income_statement.other_utility, + 'non_utility_expense': current_income_statement.non_utility_expense, + 'net_non_energy_opex': current_income_statement.net_non_energy_opex, + 'total_opex': current_income_statement.total_opex, + 'noi': current_income_statement.noi + } + return table_dict + + def get_total_energy_dict(self): + total_energy_dict = {} + for current_income_statement in self.table: + total_energy_dict[ + current_income_statement.year] = current_income_statement.energy_opex + return total_energy_dict + + +def validate_growth_rate_flag(growth_rate_flag): + """ + Validate growth_rate_flag + the input is either -2, -1 or >= 0 + """ + if growth_rate_flag >= 0: + return True + if growth_rate_flag == -1: + return True + if growth_rate_flag == -2: + return True + return False + + +def validate_annual_bill_table(annual_bill_table): + """ + check annual bill table data + variable should have 4 keys, which are utility types + All utilities should have same year range (same starting year and length) + bill charge values are not None + """ + if not len(annual_bill_table.keys()) == len(UTILITY_TYPE_LIST): + return False + + start_year = sorted(annual_bill_table['electricity'].keys())[0] + duration = len(annual_bill_table['electricity'].keys()) + + for utility in UTILITY_TYPE_LIST: + if sorted(annual_bill_table[utility].keys())[0] != start_year: + return False + if len(annual_bill_table[utility].keys()) != duration: + return False + for charge in annual_bill_table[utility].values(): + if not charge: + return False + return True + + +def validate_empty(parameter): + """ + check empty for input parameter + """ + if not parameter: + return False + else: + return True diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index 6e20df5..6f8bfad 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -1,6 +1,10 @@ from bpfin.utilbills.bill import Bill -from bpfin.financials.financial_lib import Income_Statement_Table -# from bpfin.tests.testdata import feature_data as db +from bpfin.financials.financial_income import Income_Statement_Table +from bpfin.financials.financial_lib import Balance_Sheet_Table +from bpfin.financials.cash_balance import cash_balance +from bpfin.financials.liability import final_liability_dict +# from bpfin.financials. +from bpfin.tests.testdata import feature_data as db # import pprint @@ -181,6 +185,60 @@ def prior_income_statement_table( return prior_income, next_year_income, average_income, historical_cagr +def form_raw_balance_sheet(income_table, cash_dict, liability_dict): + """ + should be deleted when + liability, cash_balance tests are refactored and merged + Balance_Sheet is refactored + """ + raw_balance_sheet = {} + hist_income = income_table.get_hist_table() + for year in hist_income: + raw_balance_sheet[year] = { + 'cash': cash_dict[year], + 'other_debt_service': (liability_dict[year] if year in liability_dict else 0), + 'noi': hist_income[year]['noi'] + } + return raw_balance_sheet + # raw_balance_sheet_demo = {2014: {'cash': 60000, 'other_debt_service': 1000, 'noi': 18000}, + # 2015: {'cash': 62000, 'other_debt_service': 1000, 'noi': 14600}, + # 2016: {'cash': 52000, 'other_debt_service': 1000, 'noi': -3100}} + + +def budget_simulation( + analysis_date, + raw_bill_table, + raw_annual_bill_table, + raw_income_input, + growth_rate_flag, + raw_liability_input, + raw_cash_balance, + raw_loan_input_list + ): + annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date) + income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) + income_table.project(growth_rate_flag, annual_bill_table) + + cash_dict = cash_balance(analysis_date, raw_cash_balance) + print(cash_dict) #!!!! need to refactor balance_sheet immediately + liability_dict = final_liability_dict(analysis_date['proforma_start'], raw_liability_input, analysis_date['proforma_duration']) + raw_balance_sheet = form_raw_balance_sheet(income_table, cash_dict, liability_dict) + balance_sheet = Balance_Sheet_Table(raw_balance_sheet) + balance_sheet.project_balance_sheet(analysis_date, liability_dict, income_table.get_noi_dict()) + print(balance_sheet.get_cash_dict()) + + +budget_simulation( + db.analysis_date, + db.raw_bill_table, + db.raw_annual_bill_table, + db.raw_income_input, + -2.0, + db.raw_liability_input, + db.raw_cash_balance, + db.raw_loan_input_list + ) + # **** ugly test **** # print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) # print('\nannual_bill =', annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)) diff --git a/bpfin/lib/other.py b/bpfin/lib/other.py index 65e34f9..a643c73 100644 --- a/bpfin/lib/other.py +++ b/bpfin/lib/other.py @@ -4,6 +4,9 @@ import numpy as np from scipy.optimize import linprog +UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] + + def add_year_dictionary(dict_1, dict_2): summed_dict = {x: dict_1.get(x, 0) + dict_2.get(x, 0) for x in set(dict_1).union(dict_2)} return summed_dict diff --git a/bpfin/tests/test_utilbills/test_back_end_call.py b/bpfin/tests/test_utilbills/test_back_end_call.py index 8a8e695..9fb324e 100644 --- a/bpfin/tests/test_utilbills/test_back_end_call.py +++ b/bpfin/tests/test_utilbills/test_back_end_call.py @@ -6,8 +6,8 @@ from bpfin.tests.testdata import sample_data as sdb def test_monthly_bill(): output_dict = { 'electricity': db.prior_annual_bill['electricity'], - 'gas': None, - 'oil': None, + 'gas': db.prior_annual_bill['gas'], + 'oil': db.prior_annual_bill['oil'], 'water': None} result_dict = monthly_bill(db.raw_bill_table, db.analysis_date) assert output_dict == result_dict @@ -26,7 +26,6 @@ def test_prior_income_statement_table(): output_historical_cagr = db.historical_cagr prior_income, next_year_income, average_income, historical_cagr = prior_income_statement_table( db.raw_income_input, - # db.prior_annual_bill, db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date, diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 5c82598..f8c8c7c 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -1,7 +1,7 @@ + """ feature_data.py is a fake database to store demo test data """ -import datetime from datetime import date # from bpfin.utilbills.bill import form_bill_list_month # from bpfin.utilbills.bill import form_annual_bill_table @@ -14,13 +14,13 @@ import pprint # pro-forma projection time base analysis_date = { - 'proforma_start': datetime.date(2012, 1, 15), + 'proforma_start': date(2012, 1, 15), 'proforma_duration': 25} # Engineering Scenarios and project economics cost_estimation = 150000.00 -commission_date = datetime.date(2017, 3, 14) +commission_date = date(2017, 3, 14) # required debt service coverage ratio @@ -55,74 +55,178 @@ full_saving_dict = { } # dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} +raw_elec_bill_demo = {} +raw_elec_bill_demo['utility_type'] = 'electricity' +raw_elec_bill_demo['date_from'] = [ + date(2016, 11, 28), date(2016, 10, 26), date(2016, 9, 26), + date(2016, 8, 25), date(2016, 7, 27), date(2016, 6, 27), + date(2016, 5, 26), date(2016, 4, 27), date(2016, 3, 29), + date(2016, 2, 29), date(2016, 1, 28), date(2016, 12, 29), + date(2015, 11, 25), date(2015, 10, 27), date(2015, 9, 25), + date(2015, 8, 26), date(2015, 7, 28), date(2015, 6, 26), + date(2015, 5, 28), date(2015, 4, 28), date(2015, 3, 30), + date(2015, 2, 27), date(2015, 1, 28)] +raw_elec_bill_demo['date_to'] = [ + date(2016, 12, 28), date(2016, 11, 28), date(2016, 10, 26), + date(2016, 9, 26), date(2016, 8, 25), date(2016, 7, 27), + date(2016, 6, 27), date(2016, 5, 26), date(2016, 4, 27), + date(2016, 3, 29), date(2016, 2, 29), date(2016, 1, 28), + date(2016, 12, 29), date(2015, 11, 25), date(2015, 10, 27), + date(2015, 9, 25), date(2015, 8, 26), date(2015, 7, 28), + date(2015, 6, 26), date(2015, 5, 28), date(2015, 4, 28), + date(2015, 3, 30), date(2015, 2, 27)] +raw_elec_bill_demo['charge'] = [ + 56.01, 39.62, 67.42, 63.6, + 57.25, 56.21, 71.66, 68.28, + 63.57, 68.12, 71.73, 145.44, + 172.37, 130.65, 136.1, 137.14, + 126.16, 152.56, 169.68, 196.46, + 163.49, 163.88, 172.33] +raw_elec_bill_demo['usage'] = [ + 117.88, 91.93, 89.07, 98.17, + 86.88, 87.05, 101.05, 88.19, + 90.05, 104.19, 98.13, 182.55, + 208.85, 144.06, 136.16, 162.13, + 154.95, 179.64, 189.02, 168.92, + 190.8, 189.42, 214.66] -e_raw_bill = { - 'date_from': [ - date(2015, 1, 28), date(2015, 2, 27), date(2015, 3, 30), date(2015, 4, 28), - date(2015, 5, 28), date(2015, 6, 26), date(2015, 7, 28), date(2015, 8, 26), - date(2015, 9, 25), date(2015, 10, 27), date(2015, 11, 25), date(2015, 12, 29), - date(2016, 1, 28), date(2016, 2, 29), date(2016, 3, 29), date(2016, 4, 27), - date(2016, 5, 26), date(2016, 6, 27), date(2016, 7, 27), date(2016, 8, 25), - date(2016, 9, 26), date(2016, 10, 26), date(2016, 11, 28) - ], - 'date_to': [ - date(2015, 2, 27), date(2015, 3, 30), date(2015, 4, 28), date(2015, 5, 28), - date(2015, 6, 26), date(2015, 7, 28), date(2015, 8, 26), date(2015, 9, 25), - date(2015, 10, 27), date(2015, 11, 25), date(2015, 12, 29), date(2016, 1, 28), - date(2016, 2, 29), date(2016, 3, 29), date(2016, 4, 27), date(2016, 5, 26), - date(2016, 6, 27), date(2016, 7, 27), date(2016, 8, 25), date(2016, 9, 26), - date(2016, 10, 26), date(2016, 11, 28), date(2016, 12, 28) - ], - 'usage': [ - 214.66, 189.42, 190.8, 168.92, 189.02, 179.64, 154.95, 162.13, - 136.16, 144.06, 208.85, 182.55, 98.13, 104.19, 90.05, - 88.19, 101.05, 87.05, 86.88, 98.17, 89.07, 91.93, 117.88 - ], - 'charge': [ - 172.33, 163.88, 163.49, 196.46, 169.68, 152.56, 126.16, - 137.14, 136.1, 130.65, 172.37, 145.44, 71.73, 68.12, 63.57, - 68.28, 71.66, 56.21, 57.25, 63.6, 67.42, 39.62, 56.01 - ] -} +raw_gas_bill_demo = {} +raw_gas_bill_demo['utility_type'] = 'gas' +raw_gas_bill_demo['date_from'] = [ + date(2016, 11, 22), date(2016, 10, 18), date(2016, 9, 30), + date(2016, 9, 19), date(2016, 8, 18), date(2016, 7, 20), + date(2016, 6, 20), date(2016, 5, 18), date(2016, 4, 19), + date(2016, 3, 18), date(2016, 2, 17), date(2016, 1, 19), + date(2015, 12, 16), date(2015, 11, 16), date(2015, 10, 16), + date(2015, 9, 19), date(2015, 8, 18), date(2015, 7, 20), + date(2015, 6, 18), date(2015, 5, 19)] +raw_gas_bill_demo['date_to'] = [ + date(2016, 12, 16), date(2016, 11, 22), date(2016, 10, 18), + date(2016, 9, 30), date(2016, 9, 19), date(2016, 8, 18), + date(2016, 7, 20), date(2016, 6, 20), date(2016, 5, 18), + date(2016, 4, 19), date(2016, 3, 18), date(2016, 2, 17), + date(2016, 1, 19), date(2015, 12, 16), date(2015, 11, 16), + date(2015, 10, 16), date(2015, 9, 19), date(2015, 8, 18), + date(2015, 7, 20), date(2015, 6, 18)] +raw_gas_bill_demo['charge'] = [ + 304.79, 456.54, 232.75, 372.16, + 192.29, 379.36, 385.14, 400.74, + 343.49, 377.99, 378.44, 361.68, + 454.84, 397.33, 401.29, 354.76, + 412.32, 378.43, 414.33, 390.65] +raw_gas_bill_demo['usage'] = [ + 322.00, 470.00, 241.00, 148.00, + 429.00, 389.00, 402.00, 443.00, + 389.00, 429.00, 402.00, 389.00, + 456.00, 402.00, 416.00, 362.00, + 429.00, 389.00, 429.00, 402.00] -raw_bill_table = { - 'electricity': e_raw_bill, - # 'gas': None, - # 'oil': None, - # 'water': None, -} +raw_oil_bill_demo = {} +raw_oil_bill_demo['utility_type'] = 'oil' +raw_oil_bill_demo['date_from'] = [ + date(2016, 11, 28), date(2016, 10, 26), date(2016, 9, 26), + date(2016, 8, 25), date(2016, 7, 27), date(2016, 6, 27), + date(2016, 5, 26), date(2016, 4, 27), date(2016, 3, 29), + date(2016, 2, 29), date(2016, 1, 28), date(2016, 12, 29), + date(2015, 11, 25), date(2015, 10, 27), date(2015, 9, 25), + date(2015, 8, 26), date(2015, 7, 28), date(2015, 6, 26), + date(2015, 5, 28), date(2015, 4, 28), date(2015, 3, 30), + date(2015, 2, 27), date(2015, 1, 28)] +raw_oil_bill_demo['date_to'] = [ + date(2016, 12, 28), date(2016, 11, 28), date(2016, 10, 26), + date(2016, 9, 26), date(2016, 8, 25), date(2016, 7, 27), + date(2016, 6, 27), date(2016, 5, 26), date(2016, 4, 27), + date(2016, 3, 29), date(2016, 2, 29), date(2016, 1, 28), + date(2016, 12, 29), date(2015, 11, 25), date(2015, 10, 27), + date(2015, 9, 25), date(2015, 8, 26), date(2015, 7, 28), + date(2015, 6, 26), date(2015, 5, 28), date(2015, 4, 28), + date(2015, 3, 30), date(2015, 2, 27)] +raw_oil_bill_demo['charge'] = [ + 3100, 2400, 3380, 1850, + 3000, 3200, 6700, 5300, + 4850, 5130, 5700, 3120, + 2420, 3400, 1870, 3020, + 3220, 6720, 5320, 4870, + 5150, 5720, 3680] +raw_oil_bill_demo['usage'] = [ + 1200, 700, 1000, 500, + 800, 900, 1700, 1400, + 1200, 1300, 1500, 1200, + 750, 1050, 550, 850, + 950, 1750, 1450, 1250, + 1350, 1550, 1450] +raw_water_bill_demo = {} +raw_water_bill_demo['utility_type'] = 'water' +raw_water_bill_demo['date_from'] = [ + date(2016, 1, 3), date(2015, 1, 1), date(2014, 1, 2)] +raw_water_bill_demo['date_to'] = [ + date(2016, 12, 28), date(2016, 1, 2), date(2014, 12, 31)] +raw_water_bill_demo['charge'] = [ + 20000, 20500, 21000] +raw_water_bill_demo['usage'] = [ + 3500000, 3600000, 3300000] + +raw_bill_table = { + 'gas': raw_gas_bill_demo, + 'electricity': raw_elec_bill_demo, + 'oil': raw_oil_bill_demo, + 'water': None} # raw annual_bill -annual_bill_gas = {2013: None, 2014: 0, 2015: 1020, 2016: 1220, 2017: 1520} -annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} -annual_bill_water = {2015: 0, 2016: 0, 2017: 0} +# annual_bill_gas = {2014: 0, 2015: 1020, 2016: 1220, 2017: 1520} +# annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} +# annual_bill_water = {2015: 0, 2016: 0, 2017: 0} +annual_bill_water = {2014: 20000, 2015: 20500, 2016: 21000} +# annual_bill_gas = {2013: None, 2014: 0, 2015: 1020, 2016: 1220, 2017: 1520} +# annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} + raw_annual_bill_table = { - # 'electricity': None, # True == Mannual Input - 'gas': annual_bill_gas, - 'oil': annual_bill_oil, + 'electricity': None, # True == Mannual Input + 'gas': None, + 'oil': None, 'water': annual_bill_water } # raw income_statement +# raw_income_input = { +# 2014: { +# 'revenue': 90000, +# 'utility_expense': 55000, +# 'non_utility_expense': 3500 +# }, +# 2015: { +# 'revenue': 95000, +# 'utility_expense': 55000, +# 'non_utility_expense': 3000 +# }, +# 2016: { +# 'revenue': 100000, +# 'utility_expense': 60000, +# 'non_utility_expense': 3000 +# } +# } + + raw_income_input = { 2014: { - 'revenue': 90000, - 'utility_expense': 55000, - 'non_utility_expense': 3500 + 'revenue': 200000, + 'utility_expense': 77000, + 'non_utility_expense': 105000, }, 2015: { - 'revenue': 95000, - 'utility_expense': 55000, - 'non_utility_expense': 3000 + 'revenue': 196000, + 'utility_expense': 77400, + 'non_utility_expense': 104000, }, 2016: { - 'revenue': 100000, - 'utility_expense': 60000, - 'non_utility_expense': 3000 + 'revenue': 205000, + 'utility_expense': 78100, + 'non_utility_expense': 130000, } } + # saving scenarios percent_saving_dict = { 'electricity': 0.25, @@ -132,613 +236,448 @@ percent_saving_dict = { } full_saving_dict = { - 'electricity': None, # dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} + 'electricity': None, 'gas': None, 'oil': None, 'water': None } +# dict of engineering analysis, {'date_from': [], 'date_to': [], 'heating': [], 'cooling': [], 'other': []} + +raw_liability_input = { + 'debt1': (150, 'NYSERDA', 10, date(2012, 12, 31)), + 'debt2': (100, 'NYCEEC', 20, date(2012, 8, 31)) + } + +raw_cash_balance = { + date(2014, 3, 14): (60000, False), + date(2015, 12, 31): (62000, True), + date(2016, 12, 31): (52000, True) +} + + +loan_term_1 = {'institute': 'NYSERDA', 'max_amount': 500000, 'interest': 0.08, 'duration': 120} +loan_term_2 = {'institute': 'Joe Fund', 'max_amount': 50000, 'interest': 0.05, 'duration': 108} +loan_term_3 = {'institute': 'Tooraj Capital', 'max_amount': 75000, 'interest': 0.07, 'duration': 114} +loan_term_dict = {1: loan_term_1, 2: loan_term_2, 3: loan_term_3} +raw_loan_input_list = [loan_term_1, loan_term_2, loan_term_3] + +# ****** result ***** prior_annual_bill = { 'electricity': { - 2012: 1302.3894189123205, - 2013: 1325.5398892097569, - 2014: 1346.0795010524571, - 2015: 1357.4370596899435, - 2016: 1368.4645719731475, - 2017: 1395.2434398071116, - 2018: 1429.9391383624793, - 2019: 1465.1219842627888, - 2020: 1499.442976989403, - 2021: 1531.5444508417731, - 2022: 1560.6164039629107, - 2023: 1589.1048030135796, - 2024: 1618.1893852141502, - 2025: 1648.2611314796882, - 2026: 1680.5743815217027, - 2027: 1715.4591028542839, - 2028: 1751.2717401473571, - 2029: 1787.1346258078672, - 2030: 1822.9958881766795, - 2031: 1858.9654290419135, - 2032: 1895.3329028685823, - 2033: 1932.5220357027845, - 2034: 1970.7052052121689, - 2035: 2009.9298853655182, - 2036: 2050.2008318127446 + 2012: 792.50603354980865, 2013: 806.56436875723125, 2014: 819.06930386346062, 2015: 825.90552345154924, + 2016: 832.68423357284746, 2017: 849.02230670593224, 2018: 870.14337868170207, 2019: 891.54208077704823, + 2020: 912.4254645897646, 2021: 931.94007239274924, 2022: 949.62365204805212, 2023: 966.95721096995919, + 2024: 984.65685227063227, 2025: 1002.9565908187957, 2026: 1022.6293693938102, 2027: 1043.8599889408579, + 2028: 1065.650260619291, 2029: 1087.4697953564814, 2030: 1109.2892523473229, 2031: 1131.1744618622365, + 2032: 1153.3039252083101, 2033: 1175.9341789481377, 2034: 1199.1695873801598, 2035: 1223.0386652944458, + 2036: 1247.5443683668141 }, 'gas': { - 2014: 1253.3333333333333, - 2015: 1020, - 2016: 1220, - 2017: 1520, - 2012: 1253.3333333333333, - 2013: 1253.3333333333333, - 2018: 1253.3333333333333, - 2019: 1253.3333333333333, - 2020: 1253.3333333333333, - 2021: 1253.3333333333333, - 2022: 1253.3333333333333, - 2023: 1253.3333333333333, - 2024: 1253.3333333333333, - 2025: 1253.3333333333333, - 2026: 1253.3333333333333, - 2027: 1253.3333333333333, - 2028: 1253.3333333333333, - 2029: 1253.3333333333333, - 2030: 1253.3333333333333, - 2031: 1253.3333333333333, - 2032: 1253.3333333333333, - 2033: 1253.3333333333333, - 2034: 1253.3333333333333, - 2035: 1253.3333333333333, - 2036: 1253.3333333333333 + 2012: 4475.7823032289816, 2013: 4554.23035844273, 2014: 4625.0674743716318, 2015: 4661.2913287497577, + 2016: 4701.7495360589701, 2017: 4795.4503391884955, 2018: 4915.0282767633271, 2019: 5035.5379855566225, + 2020: 5153.4461029776512, 2021: 5263.0160958060724, 2022: 5362.6629176416373, 2023: 5460.5010514877649, + 2024: 5560.5157868837869, 2025: 5663.9000896622329, 2026: 5775.3403422790016, 2027: 5895.3500983195381, + 2028: 6018.354334038997, 2029: 6141.4801983648376, 2030: 6264.6360349883253, 2031: 6388.1588176948035, + 2032: 6513.1311498178793, 2033: 6640.9586307853224, 2034: 6772.2131729990733, 2035: 6907.0434291160136, + 2036: 7045.4681428808135 }, 'oil': { - 2015: 1010, - 2016: 1210, - 2017: 1510, - 2012: 1243.3333333333333, - 2013: 1243.3333333333333, - 2014: 1243.3333333333333, - 2018: 1243.3333333333333, - 2019: 1243.3333333333333, - 2020: 1243.3333333333333, - 2021: 1243.3333333333333, - 2022: 1243.3333333333333, - 2023: 1243.3333333333333, - 2024: 1243.3333333333333, - 2025: 1243.3333333333333, - 2026: 1243.3333333333333, - 2027: 1243.3333333333333, - 2028: 1243.3333333333333, - 2029: 1243.3333333333333, - 2030: 1243.3333333333333, - 2031: 1243.3333333333333, - 2032: 1243.3333333333333, - 2033: 1243.3333333333333, - 2034: 1243.3333333333333, - 2035: 1243.3333333333333, - 2036: 1243.3333333333333 + 2012: 29234.099384206329, 2013: 29755.35937994433, 2014: 30216.03963037725, 2015: 30474.979346602693, + 2016: 30718.862565999072, 2017: 31317.518165038713, 2018: 32095.810899238011, 2019: 32886.130778177096, + 2020: 33656.574871338729, 2021: 34378.237209910454, 2022: 35031.181929600622, 2023: 35670.741459090197, + 2024: 36323.497554619345, 2025: 36998.445300671876, 2026: 37723.192824355341, 2027: 38506.050354491614, + 2028: 39310.02024337896, 2029: 40115.192469629976, 2030: 40920.276277032368, 2031: 41727.798007431789, + 2032: 42544.132418124602, 2033: 43378.864396965881, 2034: 44235.89304442647, 2035: 45116.305108602697, + 2036: 46020.203841190028 }, - 'water': { - 2015: 0, - 2016: 0, - 2017: 0, - 2012: 0, - 2013: 0, - 2014: 0, - 2018: 0, - 2019: 0, - 2020: 0, - 2021: 0, - 2022: 0, - 2023: 0, - 2024: 0, - 2025: 0, - 2026: 0, - 2027: 0, - 2028: 0, - 2029: 0, - 2030: 0, - 2031: 0, - 2032: 0, - 2033: 0, - 2034: 0, - 2035: 0, - 2036: 0 + 'water': { + 2014: 20000, 2015: 20500, 2016: 21000, 2012: 20500.0, 2013: 20500.0, 2017: 20500.0, 2018: 20500.0, + 2019: 20500.0, 2020: 20500.0, 2021: 20500.0, 2022: 20500.0, 2023: 20500.0, 2024: 20500.0, 2025: 20500.0, + 2026: 20500.0, 2027: 20500.0, 2028: 20500.0, 2029: 20500.0, 2030: 20500.0, 2031: 20500.0, 2032: 20500.0, + 2033: 20500.0, 2034: 20500.0, 2035: 20500.0, 2036: 20500.0 } } + prior_income_statement_cagr = { 2014: { 'year': 2014, - 'revenue': 90000, - 'utility_expense': 55000, - 'energy_opex': 3842.7461677191232, - 'electricity_opex': 1346.0795010524571, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 51157.253832280876, - 'non_utility_expense': 3500, - 'net_non_energy_opex': 54657.253832280876, - 'total_opex': 58500.0, - 'noi': 31500.0 + 'revenue': 200000, + 'utility_expense': 77000, + 'energy_opex': 55660.176408612344, + 'electricity_opex': 819.06930386346062, + 'gas_opex': 4625.0674743716318, + 'oil_opex': 30216.03963037725, + 'water_opex': 20000, + 'other_utility': 21339.823591387656, + 'non_utility_expense': 105000, + 'net_non_energy_opex': 126339.82359138766, + 'total_opex': 182000.0, + 'noi': 18000.0 }, 2015: { 'year': 2015, - 'revenue': 95000, - 'utility_expense': 55000, - 'energy_opex': 3387.4370596899435, - 'electricity_opex': 1357.4370596899435, - 'gas_opex': 1020, - 'oil_opex': 1010, - 'water_opex': 0, - 'other_utility': 51612.562940310054, - 'non_utility_expense': 3000, - 'net_non_energy_opex': 54612.562940310054, - 'total_opex': 58000.0, - 'noi': 37000.0 + 'revenue': 196000, + 'utility_expense': 77400, + 'energy_opex': 56462.176198804002, + 'electricity_opex': 825.90552345154924, + 'gas_opex': 4661.2913287497577, + 'oil_opex': 30474.979346602693, + 'water_opex': 20500, + 'other_utility': 20937.823801195998, + 'non_utility_expense': 104000, + 'net_non_energy_opex': 124937.82380119601, + 'total_opex': 181400.0, + 'noi': 14600.0 }, 2016: { 'year': 2016, - 'revenue': 100000, - 'utility_expense': 60000, - 'energy_opex': 3798.4645719731475, - 'electricity_opex': 1368.4645719731475, - 'gas_opex': 1220, - 'oil_opex': 1210, - 'water_opex': 0, - 'other_utility': 56201.535428026851, - 'non_utility_expense': 3000, - 'net_non_energy_opex': 59201.535428026851, - 'total_opex': 63000.0, - 'noi': 37000.0 + 'revenue': 205000, + 'utility_expense': 78100, + 'energy_opex': 57253.29633563089, + 'electricity_opex': 832.68423357284746, + 'gas_opex': 4701.7495360589701, + 'oil_opex': 30718.862565999072, + 'water_opex': 21000, + 'other_utility': 20846.70366436911, + 'non_utility_expense': 130000, + 'net_non_energy_opex': 150846.70366436912, + 'total_opex': 208100.0, + 'noi': -3100.0 }, 2017: { 'year': 2017, - 'revenue': 105409.25533894598, - 'utility_expense': 63221.916617675291, - 'energy_opex': 4425.2434398071118, - 'electricity_opex': 1395.2434398071116, - 'gas_opex': 1520, - 'oil_opex': 1510, - 'water_opex': 0, - 'other_utility': 58796.673177868179, - 'non_utility_expense': 3513.6418446315324, - 'net_non_energy_opex': 62310.315022499708, - 'total_opex': 66735.558462306813, - 'noi': 38673.696876639166 + 'revenue': 207546.681495995, + 'utility_expense': 79261.074976421922, + 'energy_opex': 57461.990810933145, + 'electricity_opex': 849.02230670593224, + 'gas_opex': 4795.4503391884955, + 'oil_opex': 31317.518165038713, + 'water_opex': 20500.0, + 'other_utility': 21799.08416548877, + 'non_utility_expense': 117068.76044449637, + 'net_non_energy_opex': 138867.84460998513, + 'total_opex': 196329.83542091827, + 'noi': 11216.846075076726 }, 2018: { 'year': 2018, - 'revenue': 111111.11111111112, - 'utility_expense': 65903.741165893778, - 'energy_opex': 3926.6058050291458, - 'electricity_opex': 1429.9391383624793, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 61977.135360864631, - 'non_utility_expense': 3703.703703703704, - 'net_non_energy_opex': 65680.839064568339, - 'total_opex': 69607.444869597486, - 'noi': 41503.666241513638 + 'revenue': 210124.99999999994, + 'utility_expense': 80450.873180044437, + 'energy_opex': 58380.98255468304, + 'electricity_opex': 870.14337868170207, + 'gas_opex': 4915.0282767633271, + 'oil_opex': 32095.810899238011, + 'water_opex': 20500.0, + 'other_utility': 22069.890625361393, + 'non_utility_expense': 118523.08652246254, + 'net_non_energy_opex': 140592.97714782393, + 'total_opex': 198973.95970250695, + 'noi': 11151.040297492989 }, 2019: { 'year': 2019, - 'revenue': 117121.3948210511, - 'utility_expense': 69291.425515227427, - 'energy_opex': 3961.7886509294549, - 'electricity_opex': 1465.1219842627888, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 65329.63686429798, - 'non_utility_expense': 3904.0464940350366, - 'net_non_energy_opex': 69233.683358333015, - 'total_opex': 73195.472009262478, - 'noi': 43925.922811788623 + 'revenue': 212735.3485333948, + 'utility_expense': 81657.272114136751, + 'energy_opex': 59313.210844510766, + 'electricity_opex': 891.54208077704823, + 'gas_opex': 5035.5379855566225, + 'oil_opex': 32886.130778177096, + 'water_opex': 20500.0, + 'other_utility': 22344.061269625981, + 'non_utility_expense': 119995.47945560873, + 'net_non_energy_opex': 142339.54072523472, + 'total_opex': 201652.75156974548, + 'noi': 11082.596963649325 }, 2020: { 'year': 2020, - 'revenue': 123456.79012345681, - 'utility_expense': 72859.593377950107, - 'energy_opex': 3996.1096436560692, - 'electricity_opex': 1499.442976989403, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 68863.483734294045, - 'non_utility_expense': 4115.22633744856, - 'net_non_energy_opex': 72978.710071742607, - 'total_opex': 76974.819715398684, - 'noi': 46481.970408058129 + 'revenue': 215378.12499999988, + 'utility_expense': 82844.084329901569, + 'energy_opex': 60222.446438906147, + 'electricity_opex': 912.4254645897646, + 'gas_opex': 5153.4461029776512, + 'oil_opex': 33656.574871338729, + 'water_opex': 20500.0, + 'other_utility': 22621.637890995422, + 'non_utility_expense': 121486.16368552408, + 'net_non_energy_opex': 144107.8015765195, + 'total_opex': 204330.24801542563, + 'noi': 11047.87698457425 }, 2021: { 'year': 2021, - 'revenue': 130134.88313450124, - 'utility_expense': 76616.696522283994, - 'energy_opex': 4028.2111175084392, - 'electricity_opex': 1531.5444508417731, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 72588.485404775551, - 'non_utility_expense': 4337.829437816708, - 'net_non_energy_opex': 76926.314842592255, - 'total_opex': 80954.525960100698, - 'noi': 49180.357174400546 + 'revenue': 218053.7322467296, + 'utility_expense': 83975.856179475901, + 'energy_opex': 61073.193378109274, + 'electricity_opex': 931.94007239274924, + 'gas_opex': 5263.0160958060724, + 'oil_opex': 34378.237209910454, + 'water_opex': 20500.0, + 'other_utility': 22902.662801366623, + 'non_utility_expense': 122995.3664419989, + 'net_non_energy_opex': 145898.02924336554, + 'total_opex': 206971.22262147482, + 'noi': 11082.509625254781 }, 2022: { 'year': 2022, - 'revenue': 137174.21124828537, - 'utility_expense': 80572.264997622973, - 'energy_opex': 4057.2830706295772, - 'electricity_opex': 1560.6164039629107, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 76514.981926993394, - 'non_utility_expense': 4572.4737082761785, - 'net_non_energy_opex': 81087.45563526958, - 'total_opex': 85144.738705899159, - 'noi': 52029.472542386211 + 'revenue': 220762.5781249998, + 'utility_expense': 85030.647337560615, + 'energy_opex': 61843.468499290313, + 'electricity_opex': 949.62365204805212, + 'gas_opex': 5362.6629176416373, + 'oil_opex': 35031.181929600622, + 'water_opex': 20500.0, + 'other_utility': 23187.178838270298, + 'non_utility_expense': 124523.31777766213, + 'net_non_energy_opex': 147710.49661593244, + 'total_opex': 209553.96511522276, + 'noi': 11208.613009777036 }, 2023: { 'year': 2023, - 'revenue': 144594.3145938903, - 'utility_expense': 84739.644141653087, - 'energy_opex': 4085.7714696802459, - 'electricity_opex': 1589.1048030135796, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 80653.872671972844, - 'non_utility_expense': 4819.81048646301, - 'net_non_energy_opex': 85473.683158435859, - 'total_opex': 89559.454628116102, - 'noi': 55034.859965774202 + 'revenue': 223505.07555289776, + 'utility_expense': 86073.429092948703, + 'energy_opex': 62598.19972154792, + 'electricity_opex': 966.95721096995919, + 'gas_opex': 5460.5010514877649, + 'oil_opex': 35670.741459090197, + 'water_opex': 20500.0, + 'other_utility': 23475.229371400783, + 'non_utility_expense': 126070.25060304883, + 'net_non_energy_opex': 149545.47997444961, + 'total_opex': 212143.67969599753, + 'noi': 11361.395856900228 }, 2024: { 'year': 2024, - 'revenue': 152415.79027587266, - 'utility_expense': 89131.502637429046, - 'energy_opex': 4114.8560518808163, - 'electricity_opex': 1618.1893852141502, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 85016.646585548224, - 'non_utility_expense': 5080.526342529089, - 'net_non_energy_opex': 90097.172928077314, - 'total_opex': 94212.028979958137, - 'noi': 58203.761295914519 + 'revenue': 226281.6425781247, + 'utility_expense': 87135.528503000809, + 'energy_opex': 63368.670193773767, + 'electricity_opex': 984.65685227063227, + 'gas_opex': 5560.5157868837869, + 'oil_opex': 36323.497554619345, + 'water_opex': 20500.0, + 'other_utility': 23766.858309227046, + 'non_utility_expense': 127636.40072210363, + 'net_non_energy_opex': 151403.25903133067, + 'total_opex': 214771.92922510445, + 'noi': 11509.713353020255 }, 2025: { 'year': 2025, - 'revenue': 160660.349548767, - 'utility_expense': 93760.341878116174, - 'energy_opex': 4144.9277981463547, - 'electricity_opex': 1648.2611314796882, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 89615.414079969822, - 'non_utility_expense': 5355.3449849589, - 'net_non_energy_opex': 94970.759064928716, - 'total_opex': 99115.686863075069, - 'noi': 61544.662685691932 + 'revenue': 229092.70244172015, + 'utility_expense': 88227.412086838711, + 'energy_opex': 64165.301981152908, + 'electricity_opex': 1002.9565908187957, + 'gas_opex': 5663.9000896622329, + 'oil_opex': 36998.445300671876, + 'water_opex': 20500.0, + 'other_utility': 24062.110105685795, + 'non_utility_expense': 129222.00686812503, + 'net_non_energy_opex': 153284.11697381083, + 'total_opex': 217449.41895496374, + 'noi': 11643.283486756409 }, 2026: { 'year': 2026, - 'revenue': 169350.87808430297, - 'utility_expense': 98640.181698797518, - 'energy_opex': 4177.2410481883689, - 'electricity_opex': 1680.5743815217027, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 94462.940650609147, - 'non_utility_expense': 5645.029269476766, - 'net_non_energy_opex': 100107.96992008592, - 'total_opex': 104285.21096827429, - 'noi': 65065.667116028679 + 'revenue': 231938.6836425778, + 'utility_expense': 89382.192302985874, + 'energy_opex': 65021.162536028154, + 'electricity_opex': 1022.6293693938102, + 'gas_opex': 5775.3403422790016, + 'oil_opex': 37723.192824355341, + 'water_opex': 20500.0, + 'other_utility': 24361.029766957719, + 'non_utility_expense': 130827.3107401562, + 'net_non_energy_opex': 155188.34050711393, + 'total_opex': 220209.50304314209, + 'noi': 11729.180599435698 }, 2027: { 'year': 2027, - 'revenue': 178511.49949863003, - 'utility_expense': 103784.80808059855, - 'energy_opex': 4212.1257695209506, - 'electricity_opex': 1715.4591028542839, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 99572.682311077602, - 'non_utility_expense': 5950.383316621001, - 'net_non_energy_opex': 105523.0656276986, - 'total_opex': 109735.19139721955, - 'noi': 68776.308101410483 + 'revenue': 234820.0200027631, + 'utility_expense': 90608.923300079943, + 'energy_opex': 65945.260441752005, + 'electricity_opex': 1043.8599889408579, + 'gas_opex': 5895.3500983195381, + 'oil_opex': 38506.050354491614, + 'water_opex': 20500.0, + 'other_utility': 24663.662858327934, + 'non_utility_expense': 132452.55703982813, + 'net_non_energy_opex': 157116.21989815607, + 'total_opex': 223061.48033990807, + 'noi': 11758.539662855037 }, 2028: { 'year': 2028, - 'revenue': 188167.6423158922, - 'utility_expense': 109206.76135193532, - 'energy_opex': 4247.9384068140234, - 'electricity_opex': 1751.2717401473571, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 104958.8229451213, - 'non_utility_expense': 6272.254743863074, - 'net_non_energy_opex': 111231.07768898437, - 'total_opex': 115479.01609579839, - 'noi': 72688.626220093822 + 'revenue': 237737.1507336422, + 'utility_expense': 91864.080349168915, + 'energy_opex': 66894.02483803725, + 'electricity_opex': 1065.650260619291, + 'gas_opex': 6018.354334038997, + 'oil_opex': 39310.02024337896, + 'water_opex': 20500.0, + 'other_utility': 24970.055511131657, + 'non_utility_expense': 134097.9935086601, + 'net_non_energy_opex': 159068.04901979174, + 'total_opex': 225962.07385782897, + 'noi': 11775.076875813218 }, 2029: { 'year': 2029, - 'revenue': 198346.1105540334, - 'utility_expense': 114920.11497144966, - 'energy_opex': 4283.8012924745335, - 'electricity_opex': 1787.1346258078672, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 110636.31367897513, - 'non_utility_expense': 6611.5370184677795, - 'net_non_energy_opex': 117247.8506974429, - 'total_opex': 121531.65198991743, - 'noi': 76814.458564115965 + 'revenue': 240690.52050283214, + 'utility_expense': 93124.396893137426, + 'energy_opex': 67844.142463351294, + 'electricity_opex': 1087.4697953564814, + 'gas_opex': 6141.4801983648376, + 'oil_opex': 40115.192469629976, + 'water_opex': 20500.0, + 'other_utility': 25280.254429786128, + 'non_utility_expense': 135763.8709658238, + 'net_non_energy_opex': 161044.12539560991, + 'total_opex': 228888.26785896119, + 'noi': 11802.252643870946 }, 2030: { 'year': 2030, - 'revenue': 209075.15812876914, - 'utility_expense': 120940.57693831145, - 'energy_opex': 4319.6625548433458, - 'electricity_opex': 1822.9958881766795, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 116620.9143834681, - 'non_utility_expense': 6969.171937625638, - 'net_non_energy_opex': 123590.08632109374, - 'total_opex': 127909.74887593709, - 'noi': 81165.409252832047 + 'revenue': 243680.5795019832, + 'utility_expense': 94388.508463277947, + 'energy_opex': 68794.201564368006, + 'electricity_opex': 1109.2892523473229, + 'gas_opex': 6264.6360349883253, + 'oil_opex': 40920.276277032368, + 'water_opex': 20500.0, + 'other_utility': 25594.306898909941, + 'non_utility_expense': 137450.44334637656, + 'net_non_energy_opex': 163044.7502452865, + 'total_opex': 231838.95180965451, + 'noi': 11841.627692328679 }, 2031: { 'year': 2031, - 'revenue': 220384.56728225935, - 'utility_expense': 127284.86951679207, - 'energy_opex': 4355.63209570858, - 'electricity_opex': 1858.9654290419135, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 122929.23742108348, - 'non_utility_expense': 7346.152242741978, - 'net_non_energy_opex': 130275.38966382547, - 'total_opex': 134631.02175953405, - 'noi': 85753.545522725297 + 'revenue': 246707.78351540287, + 'utility_expense': 95659.392077519587, + 'energy_opex': 69747.131286988821, + 'electricity_opex': 1131.1744618622365, + 'gas_opex': 6388.1588176948035, + 'oil_opex': 41727.798007431789, + 'water_opex': 20500.0, + 'other_utility': 25912.260790530774, + 'non_utility_expense': 139157.96773996935, + 'net_non_energy_opex': 165070.22853050011, + 'total_opex': 234817.35981748893, + 'noi': 11890.423697913939 }, 2032: { 'year': 2032, - 'revenue': 232305.73125418797, - 'utility_expense': 133970.79332894427, - 'energy_opex': 4391.9995695352491, - 'electricity_opex': 1895.3329028685823, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 129578.79375940903, - 'non_utility_expense': 7743.524375139598, - 'net_non_energy_opex': 137322.31813454864, - 'total_opex': 141714.31770408389, - 'noi': 90591.413550104073 + 'revenue': 249772.5939895327, + 'utility_expense': 96944.732064533484, + 'energy_opex': 70710.567493150797, + 'electricity_opex': 1153.3039252083101, + 'gas_opex': 6513.1311498178793, + 'oil_opex': 42544.132418124602, + 'water_opex': 20500.0, + 'other_utility': 26234.164571382684, + 'non_utility_expense': 140886.70443003593, + 'net_non_energy_opex': 167120.86900141861, + 'total_opex': 237831.43649456941, + 'noi': 11941.157494963292 }, 2033: { 'year': 2033, - 'revenue': 244871.74142473264, - 'utility_expense': 141017.23028135113, - 'energy_opex': 4429.1887023694508, - 'electricity_opex': 1932.5220357027845, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 136588.04157898168, - 'non_utility_expense': 8162.3913808244215, - 'net_non_energy_opex': 144750.43295980609, - 'total_opex': 149179.62166217554, - 'noi': 95692.119762557093 + 'revenue': 252875.4781032879, + 'utility_expense': 98255.82451699338, + 'energy_opex': 71695.757206699345, + 'electricity_opex': 1175.9341789481377, + 'gas_opex': 6640.9586307853224, + 'oil_opex': 43378.864396965881, + 'water_opex': 20500.0, + 'other_utility': 26560.067310294038, + 'non_utility_expense': 142636.91693346857, + 'net_non_energy_opex': 169196.98424376262, + 'total_opex': 240892.74145046197, + 'noi': 11982.73665282593 }, 2034: { 'year': 2034, - 'revenue': 258117.47917131998, - 'utility_expense': 148443.8093823333, - 'energy_opex': 4467.371871878835, - 'electricity_opex': 1970.7052052121689, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 143976.43751045447, - 'non_utility_expense': 8603.915972377332, - 'net_non_energy_opex': 152580.35348283179, - 'total_opex': 157047.72535471062, - 'noi': 101069.75381660936 + 'revenue': 256016.90883927097, + 'utility_expense': 99597.294490472937, + 'energy_opex': 72707.2758048057, + 'electricity_opex': 1199.1695873801598, + 'gas_opex': 6772.2131729990733, + 'oil_opex': 44235.89304442647, + 'water_opex': 20500.0, + 'other_utility': 26890.018685667244, + 'non_utility_expense': 144408.8720407868, + 'net_non_energy_opex': 171298.89072645406, + 'total_opex': 244006.16653125978, + 'noi': 12010.742308011191 }, 2035: { 'year': 2035, - 'revenue': 272079.71269414737, - 'utility_expense': 156271.08719534514, - 'energy_opex': 4506.5965520321843, - 'electricity_opex': 2009.9298853655182, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 151764.49064331295, - 'non_utility_expense': 9069.323756471578, - 'net_non_energy_opex': 160833.81439978452, - 'total_opex': 165340.41095181671, - 'noi': 106739.30174233066 + 'revenue': 259197.36505587003, + 'utility_expense': 100970.45619606454, + 'energy_opex': 73746.387203013161, + 'electricity_opex': 1223.0386652944458, + 'gas_opex': 6907.0434291160136, + 'oil_opex': 45116.305108602697, + 'water_opex': 20500.0, + 'other_utility': 27224.068993051384, + 'non_utility_expense': 146202.83985680525, + 'net_non_energy_opex': 173426.90884985664, + 'total_opex': 247173.29605286982, + 'noi': 12024.069003000215 }, 2036: { 'year': 2036, - 'revenue': 286797.19907924446, - 'utility_expense': 164520.68695453997, - 'energy_opex': 4546.8674984794106, - 'electricity_opex': 2050.2008318127446, - 'gas_opex': 1253.3333333333333, - 'oil_opex': 1243.3333333333333, - 'water_opex': 0, - 'other_utility': 159973.81945606056, - 'non_utility_expense': 9559.906635974816, - 'net_non_energy_opex': 169533.72609203539, - 'total_opex': 174080.59359051479, - 'noi': 112716.60548872966 + 'revenue': 262417.3315602527, + 'utility_expense': 102375.48550524657, + 'energy_opex': 74813.216352437652, + 'electricity_opex': 1247.5443683668141, + 'gas_opex': 7045.4681428808135, + 'oil_opex': 46020.203841190028, + 'water_opex': 20500.0, + 'other_utility': 27562.269152808924, + 'non_utility_expense': 148019.09384180643, + 'net_non_energy_opex': 175581.36299461537, + 'total_opex': 250394.57934705302, + 'noi': 12022.752213199681 } } next_year_income = { 'year': 2017, - 'revenue': 105409.25533894598, - 'utility_expense': 63221.916617675291, - 'energy_opex': 4425.2434398071118, - 'electricity_opex': 1395.2434398071116, - 'gas_opex': 1520, - 'oil_opex': 1510, - 'water_opex': 0, - 'other_utility': 58796.673177868179, - 'non_utility_expense': 3513.6418446315324, - 'net_non_energy_opex': 62310.315022499708, - 'total_opex': 66735.558462306813, - 'noi': 38673.696876639166 + 'revenue': 207546.681495995, + 'utility_expense': 79261.074976421922, + 'energy_opex': 57461.990810933145, + 'electricity_opex': 849.02230670593224, + 'gas_opex': 4795.4503391884955, + 'oil_opex': 31317.518165038713, + 'water_opex': 20500.0, + 'other_utility': 21799.08416548877, + 'non_utility_expense': 117068.76044449637, + 'net_non_energy_opex': 138867.84460998513, + 'total_opex': 196329.83542091827, + 'noi': 11216.846075076726 } average_income = { 'year': None, - 'revenue': 95000.0, - 'utility_expense': 56666.666666666657, - 'energy_opex': 3676.2159331274047, - 'electricity_opex': 1357.327044238516, - 'gas_opex': 1164.4444444444443, - 'oil_opex': 1154.4444444444443, - 'water_opex': 0.0, - 'other_utility': 52990.450733539255, - 'non_utility_expense': 3166.6666666666665, - 'net_non_energy_opex': 56157.11740020592, - 'total_opex': 59833.333333333321, - 'noi': 35166.666666666679 + 'revenue': 200333.33333333334, + 'utility_expense': 77500.0, + 'energy_opex': 56458.549647682405, + 'electricity_opex': 825.88635362928574, + 'gas_opex': 4662.7027797267865, + 'oil_opex': 30469.960514326336, + 'water_opex': 20500.0, + 'other_utility': 21041.450352317588, + 'non_utility_expense': 113000.00000000001, + 'net_non_energy_opex': 134041.4503523176, + 'total_opex': 190500.0, + 'noi': 9833.333333333343 } - -historical_cagr = 0.05409255338945984 - -post_annual_bill_table = { - 'electricity': { - 2012: 1302.3894189123205, - 2013: 1325.5398892097569, - 2014: 1346.0795010524571, - 2015: 1357.4370596899435, - 2016: 1368.4645719731475, - 2017: 1143.5987943889527, - 2018: 1072.4543537718594, - 2019: 1098.8414881970916, - 2020: 1124.5822327420522, - 2021: 1148.6583381313299, - 2022: 1170.4623029721829, - 2023: 1191.8286022601849, - 2024: 1213.6420389106127, - 2025: 1236.195848609766, - 2026: 1260.4307861412767, - 2027: 1286.5943271407125, - 2028: 1313.453805110518, - 2029: 1340.3509693559004, - 2030: 1367.2469161325098, - 2031: 1394.224071781435, - 2032: 1421.4996771514368, - 2033: 1449.3915267770883, - 2034: 1478.0289039091267, - 2035: 1507.4474140241387, - 2036: 1537.6506238595582 - }, - 'gas': { - 2012: 1253.3333333333333, - 2013: 1253.3333333333333, - 2014: 0, - 2015: 1020, - 2016: 1220, - 2017: 1406.0, - 2018: 1128.0, - 2019: 1128.0, - 2020: 1128.0, - 2021: 1128.0, - 2022: 1128.0, - 2023: 1128.0, - 2024: 1128.0, - 2025: 1128.0, - 2026: 1128.0, - 2027: 1128.0, - 2028: 1128.0, - 2029: 1128.0, - 2030: 1128.0, - 2031: 1128.0, - 2032: 1128.0, - 2033: 1128.0, - 2034: 1128.0, - 2035: 1128.0, - 2036: 1128.0 - }, - 'oil': { - 2012: 1243.3333333333333, - 2013: 1243.3333333333333, - 2014: 1243.3333333333333, - 2015: 1010, - 2016: 1210, - 2017: 603.9999999999999, - 2018: 248.6666666666666, - 2019: 248.6666666666666, - 2020: 248.6666666666666, - 2021: 248.6666666666666, - 2022: 248.6666666666666, - 2023: 248.6666666666666, - 2024: 248.6666666666666, - 2025: 248.6666666666666, - 2026: 248.6666666666666, - 2027: 248.6666666666666, - 2028: 248.6666666666666, - 2029: 248.6666666666666, - 2030: 248.6666666666666, - 2031: 248.6666666666666, - 2032: 248.6666666666666, - 2033: 248.6666666666666, - 2034: 248.6666666666666, - 2035: 248.6666666666666, - 2036: 248.6666666666666 - }, - 'water': { - 2012: 0, - 2013: 0, - 2014: 0, - 2015: 0, - 2016: 0, - 2017: 0.0, - 2018: 0.0, - 2019: 0.0, - 2020: 0.0, - 2021: 0.0, - 2022: 0.0, - 2023: 0.0, - 2024: 0.0, - 2025: 0.0, - 2026: 0.0, - 2027: 0.0, - 2028: 0.0, - 2029: 0.0, - 2030: 0.0, - 2031: 0.0, - 2032: 0.0, - 2033: 0.0, - 2034: 0.0, - 2035: 0.0, - 2036: 0.0 - } -} +historical_cagr = 0.01242283656582921 -- GitLab From 88fb6342c720378d7f02af5a6ff58ce2bc1efbf1 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 1 Jun 2017 17:24:57 -0400 Subject: [PATCH 05/17] Make return data structure for back-end-call --- bpfin/lib/back_end_call.py | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index 6f8bfad..c91d65f 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -239,6 +239,47 @@ budget_simulation( db.raw_loan_input_list ) +# x-axis value in graph, collumn titles in table +saving_potential_list = [0.10, 0.20, 0.30, 0.40, 0.50] + +# row titles in table, same as loan options from front end input +lender_list = ['NYSERDA', 'ABC Bank', 'Tooraj Fund', 'Down Payment (Self-finance)'] + +# first line. budget = sum of the list, show it in graph. In table, show sum and all elements +# dictioanry keys are saving_potential. +# list element is amount from a lender, last element is self-finance amount +budget_loan_only = { + 0.10: [10000, 20000, 30000, 0], + 0.20: [10000, 22000, 30000, 0], + 0.30: [10000, 24000, 30000, 0], + 0.40: [10000, 26000, 30000, 0], + 0.50: [10000, 28000, 30000, 0], +} + +budget_loan_first = { + 0.10: [10000, 20000, 30000, 0], + 0.20: [10000, 22000, 30000, 5000], + 0.30: [10000, 24000, 30000, 10000], + 0.40: [10000, 26000, 30000, 10000], + 0.50: [10000, 28000, 30000, 10000], +} + +budget_sf_first = { + 0.10: [10000, 10000, 30000, 15000], + 0.20: [10000, 18000, 30000, 15000], + 0.30: [10000, 21000, 30000, 15000], + 0.40: [10000, 23000, 30000, 15000], + 0.50: [10000, 25000, 30000, 15000], +} + +budget_sf_max = { + 0.10: [10000, 20000, 30000, 15000], + 0.20: [10000, 22000, 30000, 15000], + 0.30: [10000, 24000, 30000, 15000], + 0.40: [10000, 26000, 30000, 15000], + 0.50: [10000, 28000, 30000, 15000], +} + # **** ugly test **** # print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) # print('\nannual_bill =', annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)) -- GitLab From e93f234b7fa9c7fce903f6334a1b9a7d6354f75a Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 1 Jun 2017 17:28:30 -0400 Subject: [PATCH 06/17] Add financial_balance --- bpfin/lib/back_end_call.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index c91d65f..df6e58f 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -1,6 +1,6 @@ from bpfin.utilbills.bill import Bill from bpfin.financials.financial_income import Income_Statement_Table -from bpfin.financials.financial_lib import Balance_Sheet_Table +from bpfin.financials.financial_balance import Balance_Sheet_Table from bpfin.financials.cash_balance import cash_balance from bpfin.financials.liability import final_liability_dict # from bpfin.financials. -- GitLab From 84abaeb792d251b680f4a382a1090213059d1eea Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Thu, 1 Jun 2017 18:25:41 -0400 Subject: [PATCH 07/17] Update balance_sheet func --- bpfin/lib/back_end_call.py | 121 +++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 65 deletions(-) diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index df6e58f..a04c4de 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -185,26 +185,6 @@ def prior_income_statement_table( return prior_income, next_year_income, average_income, historical_cagr -def form_raw_balance_sheet(income_table, cash_dict, liability_dict): - """ - should be deleted when - liability, cash_balance tests are refactored and merged - Balance_Sheet is refactored - """ - raw_balance_sheet = {} - hist_income = income_table.get_hist_table() - for year in hist_income: - raw_balance_sheet[year] = { - 'cash': cash_dict[year], - 'other_debt_service': (liability_dict[year] if year in liability_dict else 0), - 'noi': hist_income[year]['noi'] - } - return raw_balance_sheet - # raw_balance_sheet_demo = {2014: {'cash': 60000, 'other_debt_service': 1000, 'noi': 18000}, - # 2015: {'cash': 62000, 'other_debt_service': 1000, 'noi': 14600}, - # 2016: {'cash': 52000, 'other_debt_service': 1000, 'noi': -3100}} - - def budget_simulation( analysis_date, raw_bill_table, @@ -215,17 +195,18 @@ def budget_simulation( raw_cash_balance, raw_loan_input_list ): + # !!! need to refactor when merge from saving-scenario refactoring annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date) income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) income_table.project(growth_rate_flag, annual_bill_table) cash_dict = cash_balance(analysis_date, raw_cash_balance) - print(cash_dict) #!!!! need to refactor balance_sheet immediately - liability_dict = final_liability_dict(analysis_date['proforma_start'], raw_liability_input, analysis_date['proforma_duration']) - raw_balance_sheet = form_raw_balance_sheet(income_table, cash_dict, liability_dict) - balance_sheet = Balance_Sheet_Table(raw_balance_sheet) + # print(cash_dict) + liability_dict = final_liability_dict(analysis_date, raw_liability_input) + # raw_balance_sheet = form_raw_balance_sheet(income_table, cash_dict, liability_dict) + balance_sheet = Balance_Sheet_Table(cash_dict, liability_dict, income_table.get_noi_dict()) balance_sheet.project_balance_sheet(analysis_date, liability_dict, income_table.get_noi_dict()) - print(balance_sheet.get_cash_dict()) + print('\nhist bs =', balance_sheet.get_hist_balance_sheet_table()) budget_simulation( @@ -239,46 +220,56 @@ budget_simulation( db.raw_loan_input_list ) -# x-axis value in graph, collumn titles in table -saving_potential_list = [0.10, 0.20, 0.30, 0.40, 0.50] - -# row titles in table, same as loan options from front end input -lender_list = ['NYSERDA', 'ABC Bank', 'Tooraj Fund', 'Down Payment (Self-finance)'] - -# first line. budget = sum of the list, show it in graph. In table, show sum and all elements -# dictioanry keys are saving_potential. -# list element is amount from a lender, last element is self-finance amount -budget_loan_only = { - 0.10: [10000, 20000, 30000, 0], - 0.20: [10000, 22000, 30000, 0], - 0.30: [10000, 24000, 30000, 0], - 0.40: [10000, 26000, 30000, 0], - 0.50: [10000, 28000, 30000, 0], -} - -budget_loan_first = { - 0.10: [10000, 20000, 30000, 0], - 0.20: [10000, 22000, 30000, 5000], - 0.30: [10000, 24000, 30000, 10000], - 0.40: [10000, 26000, 30000, 10000], - 0.50: [10000, 28000, 30000, 10000], -} - -budget_sf_first = { - 0.10: [10000, 10000, 30000, 15000], - 0.20: [10000, 18000, 30000, 15000], - 0.30: [10000, 21000, 30000, 15000], - 0.40: [10000, 23000, 30000, 15000], - 0.50: [10000, 25000, 30000, 15000], -} - -budget_sf_max = { - 0.10: [10000, 20000, 30000, 15000], - 0.20: [10000, 22000, 30000, 15000], - 0.30: [10000, 24000, 30000, 15000], - 0.40: [10000, 26000, 30000, 15000], - 0.50: [10000, 28000, 30000, 15000], -} +# # x-axis value in graph, collumn titles in table +# saving_potential_list = [0.10, 0.20, 0.30, 0.40, 0.50] + +# # row titles in table, same as loan options from front end input +# lender_list = ['NYSERDA', 'ABC Bank', 'Tooraj Fund', 'Down Payment (Self-finance)'] + +# # first line. budget = sum of the list, show it in graph. In table, show sum and all elements +# # dictioanry keys are saving_potential. +# # list element is amount from a lender, last element is self-finance amount +# budget_loan_only = { +# 0.10: [10000, 20000, 30000, 0], +# 0.20: [10000, 22000, 30000, 0], +# 0.30: [10000, 24000, 30000, 0], +# 0.40: [10000, 26000, 30000, 0], +# 0.50: [10000, 28000, 30000, 0], +# } + +# budget_loan_first = { +# 0.10: [10000, 20000, 30000, 0], +# 0.20: [10000, 22000, 30000, 5000], +# 0.30: [10000, 24000, 30000, 10000], +# 0.40: [10000, 26000, 30000, 10000], +# 0.50: [10000, 28000, 30000, 10000], +# } + +# budget_sf_first = { +# 0.10: [10000, 10000, 30000, 15000], +# 0.20: [10000, 18000, 30000, 15000], +# 0.30: [10000, 21000, 30000, 15000], +# 0.40: [10000, 23000, 30000, 15000], +# 0.50: [10000, 25000, 30000, 15000], +# } + +# budget_sf_max = { +# 0.10: [10000, 20000, 30000, 15000], +# 0.20: [10000, 22000, 30000, 15000], +# 0.30: [10000, 24000, 30000, 15000], +# 0.40: [10000, 26000, 30000, 15000], +# 0.50: [10000, 28000, 30000, 15000], +# } + +# return 4 lists, and a list of saving_potential_list +# [ +# [budget, 100, 200, 300, 400, 500], +# ['loan 1', 100, 200, 300, 400, 500], +# ['loan 2', 100, 200, 300, 400, 500], +# ['loan 3', 100, 200, 300, 400, 500], +# ['self-f', 100, 200, 300, 400, 500] +# ] * 4 + # **** ugly test **** # print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) -- GitLab From 376ea3498a1dbb22c9b23e650c1df34687e6981d Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 2 Jun 2017 01:42:48 -0400 Subject: [PATCH 08/17] Format outputs in back_end_call --- .../financials/financial_budget_simulator.py | 57 ++++--- bpfin/financials/loan.py | 3 + bpfin/lib/back_end_call.py | 141 +++++++++--------- bpfin/tests/testdata/feature_data.py | 4 +- 4 files changed, 114 insertions(+), 91 deletions(-) diff --git a/bpfin/financials/financial_budget_simulator.py b/bpfin/financials/financial_budget_simulator.py index ba60bb9..4100973 100644 --- a/bpfin/financials/financial_budget_simulator.py +++ b/bpfin/financials/financial_budget_simulator.py @@ -77,8 +77,7 @@ def cal_max_budget( dscr == debt service coverage ratio To do: calculate self_finance amount - if customer wants to self-finance, but loan first -> SF = cost - sum(xi) - if customer wants to S-F, but SF first -> max(xi) = cost - SF_max + rewrite descriptions """ # sum_loan_max_amount = sum(list(current_loan.max_amount for current_loan in loan_list)) # # pre-request judge: loan_list has a length @@ -122,10 +121,10 @@ def cal_max_budget( # print('\n saving allow =', (savings/req_dscr['req_saving_dscr'] - total_ds) * expected_payback / 12) downpayment = min((savings/req_dscr['req_saving_dscr'] - total_ds) * expected_payback / 12, downpayment_max) res.x[-1] = downpayment - return res.x + return list(res.x) -def form_budget( +def form_max_financing( loan_list, downpayment_max, expected_payback, @@ -139,10 +138,10 @@ def form_budget( dictionary: dict of tuples. each tuple represents a financing plan, with and budget, financing plan Description: budget = { - 'loan_only': (max_budget, [x[1], x[2], x[3], ..., x[n]]), - 'loan_first': (max_budget, [x[1], x[2], x[3], ..., x[n]]), - 'sf_first': (), - 'sf_max': () + 'loan_only': [max_budget, x[1], x[2], x[3], ..., x[n]], + 'loan_first': [max_budget, x[1], x[2], x[3], ..., x[n]], + 'sf_first': [], + 'sf_max': [] } """ budget = {} @@ -156,7 +155,8 @@ def form_budget( savings=savings, is_loan_first=False ) - budget['loan_only'] = (sum(result), list(result)) + result.insert(0, sum(result)) + budget['loan_only'] = result result = cal_max_budget( loan_list=loan_list, @@ -168,7 +168,8 @@ def form_budget( savings=savings, is_loan_first=True ) - budget['loan_first'] = (sum(result), list(result)) + result.insert(0, sum(result)) + budget['loan_first'] = result result = cal_max_budget( loan_list=loan_list, @@ -180,7 +181,8 @@ def form_budget( savings=savings, is_loan_first=False ) - budget['sf_first'] = (sum(result), list(result)) + result.insert(0, sum(result)) + budget['sf_first'] = result result = cal_max_budget( loan_list=loan_list, @@ -192,19 +194,34 @@ def form_budget( savings=savings, is_loan_first=False ) - budget['sf_max'] = (sum(result), list(result)) + result.insert(0, sum(result)) + budget['sf_max'] = result return budget -print(form_budget( - loan_list=Loan_List(db.loan_input_list).loan_list, - downpayment_max=90000, - expected_payback=120, - req_dscr=db.req_dscr, - first_year_noi=10000, - first_year_cash=10000, - savings=15000)) +def form_budget_simulation_result(loan_list): + preference_list = ['loan_only', 'loan_first', 'sf_first', 'sf_max'] + budget_simulation_result = {} + for preference in preference_list: + table_lists = [] + table_lists.append(['saving_percentage']) + table_lists.append(['budget']) + for loan in loan_list: + table_lists.append([loan.get_loan_terms()['institute']]) + table_lists.append(['self_finance']) + budget_simulation_result[preference] = table_lists + return budget_simulation_result + + +# print(form_budget( +# loan_list=Loan_List(db.loan_input_list).loan_list, +# downpayment_max=90000, +# expected_payback=120, +# req_dscr=db.req_dscr, +# first_year_noi=10000, +# first_year_cash=10000, +# savings=15000)) # ******** the following is draft for old work, for budge calculator development ******** diff --git a/bpfin/financials/loan.py b/bpfin/financials/loan.py index 966ed12..76b8fae 100644 --- a/bpfin/financials/loan.py +++ b/bpfin/financials/loan.py @@ -196,6 +196,9 @@ class Loan_List(): current_loan.originate() return allocated_loan_list + def get_loan_list(self): + return self.loan_list + # ****** ugly tests Loan and Loan_List class ********** # loan_list = Loan_List(db.loan_input_list) diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index a04c4de..15869bc 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -1,9 +1,11 @@ +import copy from bpfin.utilbills.bill import Bill from bpfin.financials.financial_income import Income_Statement_Table from bpfin.financials.financial_balance import Balance_Sheet_Table from bpfin.financials.cash_balance import cash_balance from bpfin.financials.liability import final_liability_dict -# from bpfin.financials. +from bpfin.financials.loan import Loan_List +from bpfin.financials.financial_budget_simulator import cal_max_budget, form_max_financing, form_budget_simulation_result from bpfin.tests.testdata import feature_data as db # import pprint @@ -187,88 +189,71 @@ def prior_income_statement_table( def budget_simulation( analysis_date, + commission_date, + customer_preference, + req_dscr, raw_bill_table, raw_annual_bill_table, raw_income_input, growth_rate_flag, raw_liability_input, raw_cash_balance, - raw_loan_input_list - ): - # !!! need to refactor when merge from saving-scenario refactoring + raw_loan_input_list): + """ + Generate budget simulation data, to draw graph and to put in table + To Do: + Need to refactor when merge from saving-scenario refactoring + Need to validate data, to allow a lot of empty inputs + saving_interval will be read from frond-end in next version + """ + preference_list = ['loan_only', 'loan_first', 'sf_first', 'sf_max'] + annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date) + income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) income_table.project(growth_rate_flag, annual_bill_table) cash_dict = cash_balance(analysis_date, raw_cash_balance) - # print(cash_dict) liability_dict = final_liability_dict(analysis_date, raw_liability_input) - # raw_balance_sheet = form_raw_balance_sheet(income_table, cash_dict, liability_dict) balance_sheet = Balance_Sheet_Table(cash_dict, liability_dict, income_table.get_noi_dict()) balance_sheet.project_balance_sheet(analysis_date, liability_dict, income_table.get_noi_dict()) - print('\nhist bs =', balance_sheet.get_hist_balance_sheet_table()) - - -budget_simulation( - db.analysis_date, - db.raw_bill_table, - db.raw_annual_bill_table, - db.raw_income_input, - -2.0, - db.raw_liability_input, - db.raw_cash_balance, - db.raw_loan_input_list - ) - -# # x-axis value in graph, collumn titles in table -# saving_potential_list = [0.10, 0.20, 0.30, 0.40, 0.50] - -# # row titles in table, same as loan options from front end input -# lender_list = ['NYSERDA', 'ABC Bank', 'Tooraj Fund', 'Down Payment (Self-finance)'] - -# # first line. budget = sum of the list, show it in graph. In table, show sum and all elements -# # dictioanry keys are saving_potential. -# # list element is amount from a lender, last element is self-finance amount -# budget_loan_only = { -# 0.10: [10000, 20000, 30000, 0], -# 0.20: [10000, 22000, 30000, 0], -# 0.30: [10000, 24000, 30000, 0], -# 0.40: [10000, 26000, 30000, 0], -# 0.50: [10000, 28000, 30000, 0], -# } - -# budget_loan_first = { -# 0.10: [10000, 20000, 30000, 0], -# 0.20: [10000, 22000, 30000, 5000], -# 0.30: [10000, 24000, 30000, 10000], -# 0.40: [10000, 26000, 30000, 10000], -# 0.50: [10000, 28000, 30000, 10000], -# } - -# budget_sf_first = { -# 0.10: [10000, 10000, 30000, 15000], -# 0.20: [10000, 18000, 30000, 15000], -# 0.30: [10000, 21000, 30000, 15000], -# 0.40: [10000, 23000, 30000, 15000], -# 0.50: [10000, 25000, 30000, 15000], -# } - -# budget_sf_max = { -# 0.10: [10000, 20000, 30000, 15000], -# 0.20: [10000, 22000, 30000, 15000], -# 0.30: [10000, 24000, 30000, 15000], -# 0.40: [10000, 26000, 30000, 15000], -# 0.50: [10000, 28000, 30000, 15000], -# } - -# return 4 lists, and a list of saving_potential_list -# [ -# [budget, 100, 200, 300, 400, 500], -# ['loan 1', 100, 200, 300, 400, 500], -# ['loan 2', 100, 200, 300, 400, 500], -# ['loan 3', 100, 200, 300, 400, 500], -# ['self-f', 100, 200, 300, 400, 500] -# ] * 4 + + loan_list = Loan_List(raw_loan_input_list).get_loan_list() + + first_year_energy = income_table.get_total_energy_dict()[commission_date.year + 1] + + first_year_cash = balance_sheet.get_first_year_cash(commission_date) + first_year_noi = income_table.get_first_year_noi(commission_date) + + saving_interval = 0.1000 + saving_potential_list = [] + interval = saving_interval + i = 0 + while interval <= 0.5: + i += 1 + saving_potential_list.append(interval) + interval += saving_interval + if i >= 9999: + break + + budget_simulation_result = form_budget_simulation_result(loan_list) + + for saving_percent in saving_potential_list: + financing_per_save = form_max_financing( + loan_list=loan_list, + downpayment_max=customer_preference['downpayment_max'], + expected_payback=customer_preference['expected_payback'], + req_dscr=req_dscr, + first_year_noi=first_year_noi, + first_year_cash=first_year_cash, + savings=first_year_energy * saving_percent + ) + for preference in preference_list: + amount_list = copy.deepcopy(financing_per_save[preference]) + amount_list.insert(0, saving_percent) + for current_list, amount in zip(budget_simulation_result[preference], amount_list): + current_list.append(amount) + return budget_simulation_result # **** ugly test **** @@ -292,3 +277,21 @@ budget_simulation( # f = open('data_generation.py', 'w') # f.write(writein) # f.close() + +# finalresult = budget_simulation( +# db.analysis_date, +# db.commission_date, +# db.customer_preference, +# db.req_dscr, +# db.raw_bill_table, +# db.raw_annual_bill_table, +# db.raw_income_input, +# -2.0, +# db.raw_liability_input, +# db.raw_cash_balance, +# db.raw_loan_input_list +# ) + +# import pprint +# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) +# pp.pprint(finalresult) diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index f8c8c7c..1f42377 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -6,7 +6,7 @@ from datetime import date # from bpfin.utilbills.bill import form_bill_list_month # from bpfin.utilbills.bill import form_annual_bill_table # from bpfin.utilbills.bill import Bill_Table -import pprint +# import pprint # one big decision is, one frontend request should either call data from database, or frontend. @@ -33,7 +33,7 @@ req_dscr = { # Customer Preference customer_preference = { - 'downpayment_max': 5000.00, + 'downpayment_max': 50000.00, 'expected_payback': 120, # in months 'cust_saving_dscr': 1.15 } -- GitLab From 37346d1979ec6e1be3eb7a7c48937b11c5d8bb81 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 2 Jun 2017 01:48:33 -0400 Subject: [PATCH 09/17] Add description --- bpfin/lib/back_end_call.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index 15869bc..cb56479 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -204,6 +204,7 @@ def budget_simulation( To Do: Need to refactor when merge from saving-scenario refactoring Need to validate data, to allow a lot of empty inputs + Write test file saving_interval will be read from frond-end in next version """ preference_list = ['loan_only', 'loan_first', 'sf_first', 'sf_max'] -- GitLab From 4f76f61054bc8ce19e0ebdae994eb742f5f72c45 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 2 Jun 2017 02:44:51 -0400 Subject: [PATCH 10/17] update back_end_call from saving_refactoring work --- bpfin/lib/back_end_call.py | 335 +++++++++++++++--- .../test_utilbills/test_back_end_call.py | 10 +- 2 files changed, 289 insertions(+), 56 deletions(-) diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index cb56479..1c616d8 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -5,13 +5,13 @@ from bpfin.financials.financial_balance import Balance_Sheet_Table from bpfin.financials.cash_balance import cash_balance from bpfin.financials.liability import final_liability_dict from bpfin.financials.loan import Loan_List +from bpfin.lib.other import UTILITY_TYPE_LIST from bpfin.financials.financial_budget_simulator import cal_max_budget, form_max_financing, form_budget_simulation_result from bpfin.tests.testdata import feature_data as db # import pprint -UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] - +# UTILITY_TYPE_LIST = ['electricity', 'gas', 'oil', 'water'] # Bill Overview Monthly Input def monthly_bill(raw_bill_table, analysis_date): @@ -19,15 +19,12 @@ def monthly_bill(raw_bill_table, analysis_date): Take in raw_bill_table Generate annual bill for utility type with available bill Generate manual_input dictionary indicating manual input label for 4 utilities - Args: raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types analysis_date (dictionary): proforma's starting date and the years of proforma - Returns: bill_overview_dict (dictionary): dictionary of dictionary of annual bills, see description for detail # manual_input_dict (dictionary): dictionary of boolean values, keys are utilities - Error Validate: raw_bill_table, analysis_date == None, Raise empty error bill_overview_dict == None or missing utility, Raise calculation error @@ -37,7 +34,6 @@ def monthly_bill(raw_bill_table, analysis_date): raw_bill != None: raw_bill < 12 months, no annual bill generated, manual_input == True raw_bill > 12 months, annual bill generated, manual_input == False - Description: raw_bill_table contains 4 utilities raw bill data. Each utility is a dictionary with 4 keys. Each utility can be None. See below for detail @@ -52,19 +48,22 @@ def monthly_bill(raw_bill_table, analysis_date): 'oil': None, 'water': None } - bill_overview_dict = { 'electricity': {2014: 100, 2015:200, ...}, 'oil': dict of oil_bill, 'gas': dict of gas_bill, 'water': dict of water_bill} + To Do: from utilbills.bill.py merge form_prior_month_bill(), and delete that one + * this work is done. need all units test and then can delete that """ - if not raw_bill_table: - raise ValueError('Bill_Overview - monthly_bill has empty raw_bill_table') + # if not raw_bill_table: + # raise ValueError( + # 'Bill_Overview - monthly_bill has empty raw_bill_table') if not analysis_date: raise ValueError('Bill_Overview - monthly_bill has empty analysis_date') bill_overview_dict = {} manual_input_dict = {} + prior_month_bill_dict = {} for utility in UTILITY_TYPE_LIST: current_bill = Bill(utility, analysis_date) if utility in raw_bill_table: @@ -73,27 +72,29 @@ def monthly_bill(raw_bill_table, analysis_date): current_bill.put_month_bill(None) bill_overview_dict[utility] = current_bill.get_annual_bill() manual_input_dict[utility] = current_bill.get_manual_input() + prior_month_bill_dict[utility] = current_bill.get_prior_proj_rough() if len(bill_overview_dict.keys()) != len(UTILITY_TYPE_LIST): - raise ValueError('Bill_Overview - monthly_bill has incomplete result in bill_overview_dict') - # if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): - # raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') - # return bill_overview_dict, manual_input_dict - return bill_overview_dict + raise ValueError( + 'Bill_Overview - monthly_bill has incomplete result in bill_overview_dict' + ) + if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): + raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') + if len(prior_month_bill_dict.keys()) != len(UTILITY_TYPE_LIST): + raise ValueError('Bill_Overview - monthly_bill has incomplete result of monthly_proj') + return bill_overview_dict, manual_input_dict, prior_month_bill_dict + # return bill_overview_dict def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): """ Take in raw_annual_bill_table Generate annual bill for utility type with and without available bill - Args: raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types analysis_date (dictionary): proforma's starting date and the years of proforma raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required - Returns: annual_bill_table (dictionary): dictionary of dictionary of annual bills, see description for detail - Description: raw_annual_bill_table = { 'electricity': {2014: 100, 2015:200, ...}, @@ -106,14 +107,17 @@ def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): 'gas': dict of gas_bill, 'water': dict of water_bill} """ - if not raw_bill_table: - raise ValueError('Bill_Overview - annual_bill has empty raw_bill_table') - if not raw_annual_bill_table: - raise ValueError('Bill_Overview - annual_bill has empty raw_annual_bill_table') + # if not raw_bill_table: + # raise ValueError( + # 'Bill_Overview - annual_bill has empty raw_bill_table') + # if not raw_annual_bill_table: + # raise ValueError( + # 'Bill_Overview - annual_bill has empty raw_annual_bill_table') if not analysis_date: raise ValueError('Bill_Overview - annual_bill has empty analysis_date') annual_bill_table = {} + manual_input_dict = {} for utility in UTILITY_TYPE_LIST: current_bill = Bill(utility, analysis_date) if utility in raw_bill_table: @@ -123,12 +127,18 @@ def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): if utility in raw_annual_bill_table: current_bill.put_annual_bill(raw_annual_bill_table[utility]) annual_bill_table[utility] = current_bill.get_annual_bill() + manual_input_dict[utility] = current_bill.get_manual_input() if len(annual_bill_table.keys()) != len(UTILITY_TYPE_LIST): - raise ValueError('Bill_Overview - annual_bill has incomplete result in annual_bill_table') - return annual_bill_table + raise ValueError( + 'Bill_Overview - annual_bill has incomplete result in annual_bill_table' + ) + if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): + raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') + return annual_bill_table, manual_input_dict + # return annual_bill_table -def prior_income_statement_table( +def form_prior_income_table( raw_income_input, raw_bill_table, raw_annual_bill_table, @@ -138,23 +148,19 @@ def prior_income_statement_table( Take in prior-annual_bill_table, and raw inputs of income statements Generate historical income statement, calculate its characters including CAGR and other percent ratios Generate prior_saving income statement. Store characters in database - Args: raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required analysis_date (dictionary): proforma's starting date and the years of proforma growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average - Returns: prior_income (dictionary): dict of dict of prior_saving income statement. Key is year next_year_income (dictionary): next year income statement. Keys are income statement items. average_income (dictionary): single year income statement for historical average. Keys = income statement items historical_cagr (float): compound annual growth rate - Description: raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} - raw_bill_table = { 'electricity': { 'date_from': list of date, @@ -166,17 +172,14 @@ def prior_income_statement_table( 'oil': None, 'water': None } - raw_annual_bill_table = { 'electricity': None 'gas': {2014: 100, 2015:200, ...}, 'oil': {2014: 100, 2015:200, ...}, 'water': dict of water_bill} - - prior_income_statement = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} - + prior_income = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} """ - annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date) + annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date)[0] income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) income_table.project(growth_rate_flag, annual_bill_table) @@ -187,6 +190,236 @@ def prior_income_statement_table( return prior_income, next_year_income, average_income, historical_cagr +def prelim_scenario(raw_bill_table, raw_annual_bill_table, + raw_income_input, growth_rate_flag, + raw_liability_input, raw_cash_balance, + raw_loan_input_list, + analysis_date, commission_date, construction_cost, + percent_saving_dict, full_saving_dict, + req_dscr, customer_preference): + from bpfin.financials.scenario import Scenario + prior_annual_bill_table, manual_input_dict = annual_bill( + raw_bill_table, raw_annual_bill_table, analysis_date + ) + + prior_income_table = Income_Statement_Table(raw_income_input, prior_annual_bill_table, analysis_date) + + cash_balance_dict = cash_balance(analysis_date, raw_cash_balance) + liability_dict = final_liability_dict(analysis_date, raw_liability_input) + + balance_sheet_input = { + 'cash_dictionary': cash_balance_dict, + 'other_debt_service_dictionary': liability_dict, + 'net_income_dictionary': prior_income_table.get_noi_dict() + } + + prior_income_table.project(growth_rate_flag, prior_annual_bill_table) + + prior_bs = Balance_Sheet_Table(cash_balance_dict, liability_dict, prior_income_table.get_noi_dict()) + + prior_bs.project_balance_sheet(analysis_date, liability_dict, prior_income_table.get_noi_dict()) + + scenario_ob = Scenario( + analysis_date=analysis_date, + commission_date=commission_date, + construction_cost=construction_cost, + manual_input_dict=manual_input_dict, + prior_annual_bill_table=prior_annual_bill_table, + prior_income_statement_table=prior_income_table, + other_debt_service=liability_dict, + growth_rate_flag=growth_rate_flag, + prior_balance_sheet_table=prior_bs, + loan_input_list=raw_loan_input_list) + + scenario_ob.prelim_analysis( + prior_month_bill=monthly_bill(raw_bill_table, analysis_date)[2], + percent_saving_dict=percent_saving_dict, + full_saving_dict=full_saving_dict, + growth_rate_flag=growth_rate_flag, + req_dscr=req_dscr, + customer_preference=customer_preference) + + # Define raw_annual_bill_table as a "get" of prior_bill + # Define raw_bill_table as a "get" of prior_bill + # Delete prior_annual_bill_table + + return scenario_ob.get_graph_dict() + + +# # Bill Overview Monthly Input +# def monthly_bill(raw_bill_table, analysis_date): +# """ +# Take in raw_bill_table +# Generate annual bill for utility type with available bill +# Generate manual_input dictionary indicating manual input label for 4 utilities + +# Args: +# raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types +# analysis_date (dictionary): proforma's starting date and the years of proforma + +# Returns: +# bill_overview_dict (dictionary): dictionary of dictionary of annual bills, see description for detail +# # manual_input_dict (dictionary): dictionary of boolean values, keys are utilities + +# Error Validate: +# raw_bill_table, analysis_date == None, Raise empty error +# bill_overview_dict == None or missing utility, Raise calculation error +# manual_input_dict == None or missing utility, Raise calculation error +# Data Validate: +# raw_bill == None: no annual bill generated, manual_input == True +# raw_bill != None: +# raw_bill < 12 months, no annual bill generated, manual_input == True +# raw_bill > 12 months, annual bill generated, manual_input == False + +# Description: +# raw_bill_table contains 4 utilities raw bill data. Each utility is a dictionary with 4 keys. +# Each utility can be None. See below for detail +# raw_bill_table = { +# 'electricity': { +# 'date_from': list of date, +# 'date_to', list of date, +# 'usage', list of float, +# 'charge', list of float +# }, +# 'gas': None, +# 'oil': None, +# 'water': None +# } + +# bill_overview_dict = { +# 'electricity': {2014: 100, 2015:200, ...}, +# 'oil': dict of oil_bill, +# 'gas': dict of gas_bill, +# 'water': dict of water_bill} +# """ +# if not raw_bill_table: +# raise ValueError('Bill_Overview - monthly_bill has empty raw_bill_table') +# if not analysis_date: +# raise ValueError('Bill_Overview - monthly_bill has empty analysis_date') +# bill_overview_dict = {} +# manual_input_dict = {} +# for utility in UTILITY_TYPE_LIST: +# current_bill = Bill(utility, analysis_date) +# if utility in raw_bill_table: +# current_bill.put_month_bill(raw_bill_table[utility]) +# else: +# current_bill.put_month_bill(None) +# bill_overview_dict[utility] = current_bill.get_annual_bill() +# manual_input_dict[utility] = current_bill.get_manual_input() +# if len(bill_overview_dict.keys()) != len(UTILITY_TYPE_LIST): +# raise ValueError('Bill_Overview - monthly_bill has incomplete result in bill_overview_dict') +# # if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): +# # raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') +# # return bill_overview_dict, manual_input_dict +# return bill_overview_dict + + +# def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): +# """ +# Take in raw_annual_bill_table +# Generate annual bill for utility type with and without available bill + +# Args: +# raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types +# analysis_date (dictionary): proforma's starting date and the years of proforma +# raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required + +# Returns: +# annual_bill_table (dictionary): dictionary of dictionary of annual bills, see description for detail + +# Description: +# raw_annual_bill_table = { +# 'electricity': {2014: 100, 2015:200, ...}, +# 'oil': dict of oil_bill, +# 'gas': dict of gas_bill, +# 'water': dict of water_bill} +# annual_bill_table = { +# 'electricity': {2014: 100, 2015:200, ...}, +# 'oil': dict of oil_bill, +# 'gas': dict of gas_bill, +# 'water': dict of water_bill} +# """ +# if not raw_bill_table: +# raise ValueError('Bill_Overview - annual_bill has empty raw_bill_table') +# if not raw_annual_bill_table: +# raise ValueError('Bill_Overview - annual_bill has empty raw_annual_bill_table') +# if not analysis_date: +# raise ValueError('Bill_Overview - annual_bill has empty analysis_date') + +# annual_bill_table = {} +# for utility in UTILITY_TYPE_LIST: +# current_bill = Bill(utility, analysis_date) +# if utility in raw_bill_table: +# current_bill.put_month_bill(raw_bill_table[utility]) +# else: +# current_bill.put_month_bill(None) +# if utility in raw_annual_bill_table: +# current_bill.put_annual_bill(raw_annual_bill_table[utility]) +# annual_bill_table[utility] = current_bill.get_annual_bill() +# if len(annual_bill_table.keys()) != len(UTILITY_TYPE_LIST): +# raise ValueError('Bill_Overview - annual_bill has incomplete result in annual_bill_table') +# return annual_bill_table + + +# def prior_income_statement_table( +# raw_income_input, +# raw_bill_table, +# raw_annual_bill_table, +# analysis_date, +# growth_rate_flag): +# """ +# Take in prior-annual_bill_table, and raw inputs of income statements +# Generate historical income statement, calculate its characters including CAGR and other percent ratios +# Generate prior_saving income statement. Store characters in database + +# Args: +# raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year +# raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types +# raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required +# analysis_date (dictionary): proforma's starting date and the years of proforma +# growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average + +# Returns: +# prior_income (dictionary): dict of dict of prior_saving income statement. Key is year +# next_year_income (dictionary): next year income statement. Keys are income statement items. +# average_income (dictionary): single year income statement for historical average. Keys = income statement items +# historical_cagr (float): compound annual growth rate + +# Description: +# raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} + +# raw_bill_table = { +# 'electricity': { +# 'date_from': list of date, +# 'date_to', list of date, +# 'usage', list of float, +# 'charge', list of float +# }, +# 'gas': None, +# 'oil': None, +# 'water': None +# } + +# raw_annual_bill_table = { +# 'electricity': None +# 'gas': {2014: 100, 2015:200, ...}, +# 'oil': {2014: 100, 2015:200, ...}, +# 'water': dict of water_bill} + +# prior_income_statement = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} + +# """ +# annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date) +# income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) +# income_table.project(growth_rate_flag, annual_bill_table) + +# prior_income = income_table.get_full_income_table() +# next_year_income = income_table.get_single_year(income_table.hist_end_year + 1) +# average_income = income_table.get_average() +# historical_cagr = income_table.get_cagr() +# return prior_income, next_year_income, average_income, historical_cagr + + def budget_simulation( analysis_date, commission_date, @@ -209,7 +442,7 @@ def budget_simulation( """ preference_list = ['loan_only', 'loan_first', 'sf_first', 'sf_max'] - annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date) + annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date)[0] income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) income_table.project(growth_rate_flag, annual_bill_table) @@ -279,20 +512,20 @@ def budget_simulation( # f.write(writein) # f.close() -# finalresult = budget_simulation( -# db.analysis_date, -# db.commission_date, -# db.customer_preference, -# db.req_dscr, -# db.raw_bill_table, -# db.raw_annual_bill_table, -# db.raw_income_input, -# -2.0, -# db.raw_liability_input, -# db.raw_cash_balance, -# db.raw_loan_input_list -# ) - -# import pprint -# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) -# pp.pprint(finalresult) +finalresult = budget_simulation( + db.analysis_date, + db.commission_date, + db.customer_preference, + db.req_dscr, + db.raw_bill_table, + db.raw_annual_bill_table, + db.raw_income_input, + -2.0, + db.raw_liability_input, + db.raw_cash_balance, + db.raw_loan_input_list + ) + +import pprint +pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) +pp.pprint(finalresult) diff --git a/bpfin/tests/test_utilbills/test_back_end_call.py b/bpfin/tests/test_utilbills/test_back_end_call.py index 9fb324e..b23b975 100644 --- a/bpfin/tests/test_utilbills/test_back_end_call.py +++ b/bpfin/tests/test_utilbills/test_back_end_call.py @@ -1,4 +1,4 @@ -from bpfin.lib.back_end_call import monthly_bill, annual_bill, prior_income_statement_table +from bpfin.lib.back_end_call import monthly_bill, annual_bill, form_prior_income_table from bpfin.tests.testdata import feature_data as db from bpfin.tests.testdata import sample_data as sdb @@ -9,22 +9,22 @@ def test_monthly_bill(): 'gas': db.prior_annual_bill['gas'], 'oil': db.prior_annual_bill['oil'], 'water': None} - result_dict = monthly_bill(db.raw_bill_table, db.analysis_date) + result_dict = monthly_bill(db.raw_bill_table, db.analysis_date)[0] assert output_dict == result_dict def test_annual_bill(): output_dict = db.prior_annual_bill - result_dict = annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date) + result_dict = annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)[0] assert output_dict == result_dict -def test_prior_income_statement_table(): +def test_form_prior_income_table(): output_income_statement = db.prior_income_statement_cagr output_next_year_income = db.next_year_income output_average_income = db.average_income output_historical_cagr = db.historical_cagr - prior_income, next_year_income, average_income, historical_cagr = prior_income_statement_table( + prior_income, next_year_income, average_income, historical_cagr = form_prior_income_table( db.raw_income_input, db.raw_bill_table, db.raw_annual_bill_table, -- GitLab From 83cf0a64670fb4873b5a5bb39bb0723d46d27a93 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 2 Jun 2017 02:50:18 -0400 Subject: [PATCH 11/17] Copy from back-end-call scenario --- bpfin/financials/scenario.py | 2 +- bpfin/lib/back_end_call.py | 312 ++++++++++++++++++----------------- 2 files changed, 159 insertions(+), 155 deletions(-) diff --git a/bpfin/financials/scenario.py b/bpfin/financials/scenario.py index bdce9e1..3310652 100644 --- a/bpfin/financials/scenario.py +++ b/bpfin/financials/scenario.py @@ -77,7 +77,7 @@ class Scenario(): commissioning_date=copy.deepcopy(commission_date)) self.scheduled_loan_list = None - def prelim_anlaysis(self, prior_month_bill, percent_saving_dict, + def prelim_analysis(self, prior_month_bill, percent_saving_dict, full_saving_dict, growth_rate_flag, req_dscr, customer_preference): """ diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index 1c616d8..2b4f71e 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -4,6 +4,7 @@ from bpfin.financials.financial_income import Income_Statement_Table from bpfin.financials.financial_balance import Balance_Sheet_Table from bpfin.financials.cash_balance import cash_balance from bpfin.financials.liability import final_liability_dict +from bpfin.financials.scenario import Scenario from bpfin.financials.loan import Loan_List from bpfin.lib.other import UTILITY_TYPE_LIST from bpfin.financials.financial_budget_simulator import cal_max_budget, form_max_financing, form_budget_simulation_result @@ -190,62 +191,176 @@ def form_prior_income_table( return prior_income, next_year_income, average_income, historical_cagr -def prelim_scenario(raw_bill_table, raw_annual_bill_table, - raw_income_input, growth_rate_flag, - raw_liability_input, raw_cash_balance, - raw_loan_input_list, - analysis_date, commission_date, construction_cost, - percent_saving_dict, full_saving_dict, - req_dscr, customer_preference): - from bpfin.financials.scenario import Scenario - prior_annual_bill_table, manual_input_dict = annual_bill( - raw_bill_table, raw_annual_bill_table, analysis_date - ) +# def prelim_scenario(raw_bill_table, raw_annual_bill_table, +# raw_income_input, growth_rate_flag, +# raw_liability_input, raw_cash_balance, +# raw_loan_input_list, +# analysis_date, commission_date, construction_cost, +# percent_saving_dict, full_saving_dict, +# req_dscr, customer_preference): +# from bpfin.financials.scenario import Scenario +# prior_annual_bill_table, manual_input_dict = annual_bill( +# raw_bill_table, raw_annual_bill_table, analysis_date +# ) + +# prior_income_table = Income_Statement_Table(raw_income_input, prior_annual_bill_table, analysis_date) + +# cash_balance_dict = cash_balance(analysis_date, raw_cash_balance) +# liability_dict = final_liability_dict(analysis_date, raw_liability_input) + +# balance_sheet_input = { +# 'cash_dictionary': cash_balance_dict, +# 'other_debt_service_dictionary': liability_dict, +# 'net_income_dictionary': prior_income_table.get_noi_dict() +# } + +# prior_income_table.project(growth_rate_flag, prior_annual_bill_table) + +# prior_bs = Balance_Sheet_Table(cash_balance_dict, liability_dict, prior_income_table.get_noi_dict()) + +# prior_bs.project_balance_sheet(analysis_date, liability_dict, prior_income_table.get_noi_dict()) + +# scenario_ob = Scenario( +# analysis_date=analysis_date, +# commission_date=commission_date, +# construction_cost=construction_cost, +# manual_input_dict=manual_input_dict, +# prior_annual_bill_table=prior_annual_bill_table, +# prior_income_statement_table=prior_income_table, +# other_debt_service=liability_dict, +# growth_rate_flag=growth_rate_flag, +# prior_balance_sheet_table=prior_bs, +# loan_input_list=raw_loan_input_list) + +# scenario_ob.prelim_analysis( +# prior_month_bill=monthly_bill(raw_bill_table, analysis_date)[2], +# percent_saving_dict=percent_saving_dict, +# full_saving_dict=full_saving_dict, +# growth_rate_flag=growth_rate_flag, +# req_dscr=req_dscr, +# customer_preference=customer_preference) + +# # Define raw_annual_bill_table as a "get" of prior_bill +# # Define raw_bill_table as a "get" of prior_bill +# # Delete prior_annual_bill_table + +# return scenario_ob.get_graph_dict() + + +def budget_simulation( + analysis_date, + commission_date, + customer_preference, + req_dscr, + raw_bill_table, + raw_annual_bill_table, + raw_income_input, + growth_rate_flag, + raw_liability_input, + raw_cash_balance, + raw_loan_input_list): + """ + Generate budget simulation data, to draw graph and to put in table + To Do: + Need to refactor when merge from saving-scenario refactoring + Need to validate data, to allow a lot of empty inputs + Write test file + saving_interval will be read from frond-end in next version + """ + preference_list = ['loan_only', 'loan_first', 'sf_first', 'sf_max'] + + annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date)[0] - prior_income_table = Income_Statement_Table(raw_income_input, prior_annual_bill_table, analysis_date) + income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) + income_table.project(growth_rate_flag, annual_bill_table) - cash_balance_dict = cash_balance(analysis_date, raw_cash_balance) + cash_dict = cash_balance(analysis_date, raw_cash_balance) liability_dict = final_liability_dict(analysis_date, raw_liability_input) + balance_sheet = Balance_Sheet_Table(cash_dict, liability_dict, income_table.get_noi_dict()) + balance_sheet.project_balance_sheet(analysis_date, liability_dict, income_table.get_noi_dict()) + + loan_list = Loan_List(raw_loan_input_list).get_loan_list() + + first_year_energy = income_table.get_total_energy_dict()[commission_date.year + 1] + + first_year_cash = balance_sheet.get_first_year_cash(commission_date) + first_year_noi = income_table.get_first_year_noi(commission_date) + + saving_interval = 0.1000 + saving_potential_list = [] + interval = saving_interval + i = 0 + while interval <= 0.5: + i += 1 + saving_potential_list.append(interval) + interval += saving_interval + if i >= 9999: + break + + budget_simulation_result = form_budget_simulation_result(loan_list) + + for saving_percent in saving_potential_list: + financing_per_save = form_max_financing( + loan_list=loan_list, + downpayment_max=customer_preference['downpayment_max'], + expected_payback=customer_preference['expected_payback'], + req_dscr=req_dscr, + first_year_noi=first_year_noi, + first_year_cash=first_year_cash, + savings=first_year_energy * saving_percent + ) + for preference in preference_list: + amount_list = copy.deepcopy(financing_per_save[preference]) + amount_list.insert(0, saving_percent) + for current_list, amount in zip(budget_simulation_result[preference], amount_list): + current_list.append(amount) + return budget_simulation_result - balance_sheet_input = { - 'cash_dictionary': cash_balance_dict, - 'other_debt_service_dictionary': liability_dict, - 'net_income_dictionary': prior_income_table.get_noi_dict() - } - prior_income_table.project(growth_rate_flag, prior_annual_bill_table) +# **** ugly test **** +# print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) +# print('\nannual_bill =', annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)) +# print('\nprior_income_statement =', prior_income_statement_table( +# db.raw_income_input, +# db.raw_bill_table, +# db.raw_annual_bill_table, +# db.analysis_date, +# -2.0)) - prior_bs = Balance_Sheet_Table(cash_balance_dict, liability_dict, prior_income_table.get_noi_dict()) +# result = prior_income_statement_table( +# db.raw_income_input, +# db.raw_bill_table, +# db.raw_annual_bill_table, +# db.analysis_date, +# -2.0) - prior_bs.project_balance_sheet(analysis_date, liability_dict, prior_income_table.get_noi_dict()) +# writein = str(result) +# f = open('data_generation.py', 'w') +# f.write(writein) +# f.close() - scenario_ob = Scenario( - analysis_date=analysis_date, - commission_date=commission_date, - construction_cost=construction_cost, - manual_input_dict=manual_input_dict, - prior_annual_bill_table=prior_annual_bill_table, - prior_income_statement_table=prior_income_table, - other_debt_service=liability_dict, - growth_rate_flag=growth_rate_flag, - prior_balance_sheet_table=prior_bs, - loan_input_list=raw_loan_input_list) +finalresult = budget_simulation( + db.analysis_date, + db.commission_date, + db.customer_preference, + db.req_dscr, + db.raw_bill_table, + db.raw_annual_bill_table, + db.raw_income_input, + -2.0, + db.raw_liability_input, + db.raw_cash_balance, + db.raw_loan_input_list + ) - scenario_ob.prelim_analysis( - prior_month_bill=monthly_bill(raw_bill_table, analysis_date)[2], - percent_saving_dict=percent_saving_dict, - full_saving_dict=full_saving_dict, - growth_rate_flag=growth_rate_flag, - req_dscr=req_dscr, - customer_preference=customer_preference) - # Define raw_annual_bill_table as a "get" of prior_bill - # Define raw_bill_table as a "get" of prior_bill - # Delete prior_annual_bill_table +import pprint +pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) +pp.pprint(finalresult) - return scenario_ob.get_graph_dict() +# **** old work **** # # Bill Overview Monthly Input # def monthly_bill(raw_bill_table, analysis_date): # """ @@ -418,114 +533,3 @@ def prelim_scenario(raw_bill_table, raw_annual_bill_table, # average_income = income_table.get_average() # historical_cagr = income_table.get_cagr() # return prior_income, next_year_income, average_income, historical_cagr - - -def budget_simulation( - analysis_date, - commission_date, - customer_preference, - req_dscr, - raw_bill_table, - raw_annual_bill_table, - raw_income_input, - growth_rate_flag, - raw_liability_input, - raw_cash_balance, - raw_loan_input_list): - """ - Generate budget simulation data, to draw graph and to put in table - To Do: - Need to refactor when merge from saving-scenario refactoring - Need to validate data, to allow a lot of empty inputs - Write test file - saving_interval will be read from frond-end in next version - """ - preference_list = ['loan_only', 'loan_first', 'sf_first', 'sf_max'] - - annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date)[0] - - income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) - income_table.project(growth_rate_flag, annual_bill_table) - - cash_dict = cash_balance(analysis_date, raw_cash_balance) - liability_dict = final_liability_dict(analysis_date, raw_liability_input) - balance_sheet = Balance_Sheet_Table(cash_dict, liability_dict, income_table.get_noi_dict()) - balance_sheet.project_balance_sheet(analysis_date, liability_dict, income_table.get_noi_dict()) - - loan_list = Loan_List(raw_loan_input_list).get_loan_list() - - first_year_energy = income_table.get_total_energy_dict()[commission_date.year + 1] - - first_year_cash = balance_sheet.get_first_year_cash(commission_date) - first_year_noi = income_table.get_first_year_noi(commission_date) - - saving_interval = 0.1000 - saving_potential_list = [] - interval = saving_interval - i = 0 - while interval <= 0.5: - i += 1 - saving_potential_list.append(interval) - interval += saving_interval - if i >= 9999: - break - - budget_simulation_result = form_budget_simulation_result(loan_list) - - for saving_percent in saving_potential_list: - financing_per_save = form_max_financing( - loan_list=loan_list, - downpayment_max=customer_preference['downpayment_max'], - expected_payback=customer_preference['expected_payback'], - req_dscr=req_dscr, - first_year_noi=first_year_noi, - first_year_cash=first_year_cash, - savings=first_year_energy * saving_percent - ) - for preference in preference_list: - amount_list = copy.deepcopy(financing_per_save[preference]) - amount_list.insert(0, saving_percent) - for current_list, amount in zip(budget_simulation_result[preference], amount_list): - current_list.append(amount) - return budget_simulation_result - - -# **** ugly test **** -# print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) -# print('\nannual_bill =', annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)) -# print('\nprior_income_statement =', prior_income_statement_table( -# db.raw_income_input, -# db.raw_bill_table, -# db.raw_annual_bill_table, -# db.analysis_date, -# -2.0)) - -# result = prior_income_statement_table( -# db.raw_income_input, -# db.raw_bill_table, -# db.raw_annual_bill_table, -# db.analysis_date, -# -2.0) - -# writein = str(result) -# f = open('data_generation.py', 'w') -# f.write(writein) -# f.close() - -finalresult = budget_simulation( - db.analysis_date, - db.commission_date, - db.customer_preference, - db.req_dscr, - db.raw_bill_table, - db.raw_annual_bill_table, - db.raw_income_input, - -2.0, - db.raw_liability_input, - db.raw_cash_balance, - db.raw_loan_input_list - ) - -import pprint -pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) -pp.pprint(finalresult) -- GitLab From d4ef1c6ee762db957fa354c4ca41dbbe237dd005 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 2 Jun 2017 03:08:00 -0400 Subject: [PATCH 12/17] Add test file for back_end_call budget simulation --- .../test_utilbills/test_back_end_call.py | 19 ++++++++ bpfin/tests/testdata/feature_data.py | 47 ++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/bpfin/tests/test_utilbills/test_back_end_call.py b/bpfin/tests/test_utilbills/test_back_end_call.py index b23b975..bc5355a 100644 --- a/bpfin/tests/test_utilbills/test_back_end_call.py +++ b/bpfin/tests/test_utilbills/test_back_end_call.py @@ -1,4 +1,5 @@ from bpfin.lib.back_end_call import monthly_bill, annual_bill, form_prior_income_table +from bpfin.lib.back_end_call import budget_simulation from bpfin.tests.testdata import feature_data as db from bpfin.tests.testdata import sample_data as sdb @@ -34,3 +35,21 @@ def test_form_prior_income_table(): assert output_next_year_income == next_year_income assert output_average_income == average_income assert output_historical_cagr == historical_cagr + + +def test_budget_simulation(): + output = db.budget_simulation_result + result = budget_simulation( + db.analysis_date, + db.commission_date, + db.customer_preference, + db.req_dscr, + db.raw_bill_table, + db.raw_annual_bill_table, + db.raw_income_input, + db.growth_rate_flag, + db.raw_liability_input, + db.raw_cash_balance, + db.raw_loan_input_list + ) + assert output == result diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 1f42377..a7eba1c 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -1,4 +1,3 @@ - """ feature_data.py is a fake database to store demo test data """ @@ -681,3 +680,49 @@ average_income = { } historical_cagr = 0.01242283656582921 +growth_rate_flag = -2.0 + +budget_simulation_result = { + 'loan_first': + [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ + 'budget', 37723.783022506701, 77213.635473036207, 118031.96226880848, + 118031.96226880848, 118031.96226880848 + ], ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0], + ['Joe Fund', 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], [ + 'Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, + 18031.962268808486, 18031.962268808486 + ], [ + 'self_finance', 9.0949470177292824e-12, 9181.6732042277363, 50000.0, + 50000.0, 50000.0 + ]], + 'loan_only': + [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ + 'budget', 37723.783022506694, 68031.962268808478, 68031.962268808478, + 68031.962268808478, 68031.962268808478 + ], ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0], + ['Joe Fund', 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], [ + 'Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, + 18031.962268808486, 18031.962268808486 + ], ['self_finance', 0.0, 0.0, 0.0, 0.0, 0.0]], + 'sf_first': + [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ + 'budget', 52184.67464428641, 89908.457666793111, 118031.96226880848, + 118031.96226880848, 118031.96226880848 + ], ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0], [ + 'Joe Fund', 2184.6746442864069, 39908.457666793111, 50000.0, 50000.0, + 50000.0 + ], [ + 'Tooraj Capital', 0.0, 0.0, 18031.962268808486, 18031.962268808486, + 18031.962268808486 + ], ['self_finance', 50000.0, 50000.0, 50000.0, 50000.0, 50000.0]], + 'sf_max': + [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ + 'budget', 87723.783022506686, 118031.96226880848, 118031.96226880848, + 118031.96226880848, 118031.96226880848 + ], ['NYSERDA', 0.0, 0.0, 0.0, 0.0, + 0.0], + ['Joe Fund', 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], [ + 'Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, + 18031.962268808486, 18031.962268808486 + ], ['self_finance', 50000.0, 50000.0, 50000.0, 50000.0, 50000.0]] +} -- GitLab From bd2ba58ac6b49f4b61715fe84af2d0508561be0a Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 2 Jun 2017 03:15:07 -0400 Subject: [PATCH 13/17] Comment ugly test --- bpfin/lib/back_end_call.py | 42 +++++++++---------- .../test_utilbills/test_back_end_call.py | 2 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py index 2b4f71e..fc76d65 100644 --- a/bpfin/lib/back_end_call.py +++ b/bpfin/lib/back_end_call.py @@ -4,11 +4,11 @@ from bpfin.financials.financial_income import Income_Statement_Table from bpfin.financials.financial_balance import Balance_Sheet_Table from bpfin.financials.cash_balance import cash_balance from bpfin.financials.liability import final_liability_dict -from bpfin.financials.scenario import Scenario +# from bpfin.financials.scenario import Scenario from bpfin.financials.loan import Loan_List from bpfin.lib.other import UTILITY_TYPE_LIST -from bpfin.financials.financial_budget_simulator import cal_max_budget, form_max_financing, form_budget_simulation_result -from bpfin.tests.testdata import feature_data as db +from bpfin.financials.financial_budget_simulator import form_max_financing, form_budget_simulation_result +# from bpfin.tests.testdata import feature_data as db # import pprint @@ -339,24 +339,24 @@ def budget_simulation( # f.write(writein) # f.close() -finalresult = budget_simulation( - db.analysis_date, - db.commission_date, - db.customer_preference, - db.req_dscr, - db.raw_bill_table, - db.raw_annual_bill_table, - db.raw_income_input, - -2.0, - db.raw_liability_input, - db.raw_cash_balance, - db.raw_loan_input_list - ) - - -import pprint -pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) -pp.pprint(finalresult) +# finalresult = budget_simulation( +# db.analysis_date, +# db.commission_date, +# db.customer_preference, +# db.req_dscr, +# db.raw_bill_table, +# db.raw_annual_bill_table, +# db.raw_income_input, +# -2.0, +# db.raw_liability_input, +# db.raw_cash_balance, +# db.raw_loan_input_list +# ) + + +# import pprint +# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) +# pp.pprint(finalresult) diff --git a/bpfin/tests/test_utilbills/test_back_end_call.py b/bpfin/tests/test_utilbills/test_back_end_call.py index bc5355a..dc972c3 100644 --- a/bpfin/tests/test_utilbills/test_back_end_call.py +++ b/bpfin/tests/test_utilbills/test_back_end_call.py @@ -1,7 +1,7 @@ from bpfin.lib.back_end_call import monthly_bill, annual_bill, form_prior_income_table from bpfin.lib.back_end_call import budget_simulation from bpfin.tests.testdata import feature_data as db -from bpfin.tests.testdata import sample_data as sdb +# from bpfin.tests.testdata import sample_data as sdb def test_monthly_bill(): -- GitLab From 9fc3308dfbdf90c8913220ee5e855ded4e3b478e Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 2 Jun 2017 12:32:40 -0400 Subject: [PATCH 14/17] Update on budget_simulation return --- bpfin/back_end_call/back_end_budget.py | 105 ++++ bpfin/back_end_call/back_end_inputs.py | 23 + bpfin/lib/back_end_call.py | 539 ------------------ .../test_back_end_budget.py | 2 +- .../test_back_end_inputs.py | 37 +- bpfin/tests/test_lib/test_back_end_call.py | 3 - 6 files changed, 147 insertions(+), 562 deletions(-) delete mode 100644 bpfin/lib/back_end_call.py delete mode 100644 bpfin/tests/test_lib/test_back_end_call.py diff --git a/bpfin/back_end_call/back_end_budget.py b/bpfin/back_end_call/back_end_budget.py index e69de29..e4dda8d 100644 --- a/bpfin/back_end_call/back_end_budget.py +++ b/bpfin/back_end_call/back_end_budget.py @@ -0,0 +1,105 @@ +import copy +from bpfin.back_end_call.back_end_inputs import annual_bill +from bpfin.financials.financial_income import Income_Statement_Table +from bpfin.financials.financial_balance import Balance_Sheet_Table +from bpfin.financials.cash_balance import cash_balance +from bpfin.financials.liability import final_liability_dict +from bpfin.financials.loan import Loan_List +from bpfin.financials.financial_budget_simulator import form_max_financing, form_budget_simulation_result + + +def budget_simulation( + analysis_date, + commission_date, + customer_preference, + req_dscr, + raw_bill_table, + raw_annual_bill_table, + raw_income_input, + growth_rate_flag, + raw_liability_input, + raw_cash_balance, + raw_loan_input_list): + """ + Generate budget simulation data, to draw graph and to put in table + To Do: + Need to refactor when merge from saving-scenario refactoring + Need to validate data, to allow a lot of empty inputs + Write test file + saving_interval will be read from frond-end in next version + """ + preference_list = ['loan_only', 'loan_first', 'sf_first', 'sf_max'] + + annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date)[0] + + income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) + income_table.project(growth_rate_flag, annual_bill_table) + + cash_dict = cash_balance(analysis_date, raw_cash_balance) + liability_dict = final_liability_dict(analysis_date, raw_liability_input) + balance_sheet = Balance_Sheet_Table(cash_dict, liability_dict, income_table.get_noi_dict()) + balance_sheet.project_balance_sheet(analysis_date, liability_dict, income_table.get_noi_dict()) + + loan_list = Loan_List(raw_loan_input_list).get_loan_list() + + first_year_energy = income_table.get_total_energy_dict()[commission_date.year + 1] + + first_year_cash = balance_sheet.get_first_year_cash(commission_date) + first_year_noi = income_table.get_first_year_noi(commission_date) + + saving_interval = 0.1000 + saving_potential_list = [] + interval = saving_interval + i = 0 + while interval <= 0.5: + i += 1 + saving_potential_list.append(interval) + interval += saving_interval + if i >= 9999: + break + + budget_simulation_result = form_budget_simulation_result(loan_list) + + for saving_percent in saving_potential_list: + financing_per_save = form_max_financing( + loan_list=loan_list, + downpayment_max=customer_preference['downpayment_max'], + expected_payback=customer_preference['expected_payback'], + req_dscr=req_dscr, + first_year_noi=first_year_noi, + first_year_cash=first_year_cash, + savings=first_year_energy * saving_percent + ) + for preference in preference_list: + amount_list = copy.deepcopy(financing_per_save[preference]) + amount_list.insert(0, saving_percent) + for current_list, amount in zip(budget_simulation_result[preference], amount_list): + current_list.append(amount) + + return ( + budget_simulation_result['loan_only'], + budget_simulation_result['loan_first'], + budget_simulation_result['sf_first'], + budget_simulation_result['sf_max']) + + +# **** ugly test **** +# from bpfin.tests.testdata import feature_data as db +# finalresult = budget_simulation( +# db.analysis_date, +# db.commission_date, +# db.customer_preference, +# db.req_dscr, +# db.raw_bill_table, +# db.raw_annual_bill_table, +# db.raw_income_input, +# -2.0, +# db.raw_liability_input, +# db.raw_cash_balance, +# db.raw_loan_input_list +# ) + + +# import pprint +# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) +# pp.pprint(finalresult) diff --git a/bpfin/back_end_call/back_end_inputs.py b/bpfin/back_end_call/back_end_inputs.py index 0b9f1fd..e70c990 100644 --- a/bpfin/back_end_call/back_end_inputs.py +++ b/bpfin/back_end_call/back_end_inputs.py @@ -199,3 +199,26 @@ def form_prior_income_table( average_income = income_table.get_average() historical_cagr = income_table.get_cagr() return prior_income, next_year_income, average_income, historical_cagr + + +# **** ugly test **** +# print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) +# print('\nannual_bill =', annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)) +# print('\nprior_income_statement =', prior_income_statement_table( +# db.raw_income_input, +# db.raw_bill_table, +# db.raw_annual_bill_table, +# db.analysis_date, +# -2.0)) + +# result = prior_income_statement_table( +# db.raw_income_input, +# db.raw_bill_table, +# db.raw_annual_bill_table, +# db.analysis_date, +# -2.0) + +# writein = str(result) +# f = open('data_generation.py', 'w') +# f.write(writein) +# f.close() diff --git a/bpfin/lib/back_end_call.py b/bpfin/lib/back_end_call.py deleted file mode 100644 index c6e5b1d..0000000 --- a/bpfin/lib/back_end_call.py +++ /dev/null @@ -1,539 +0,0 @@ -""" -This file should be transfered to back_end_call folder, and then removed. -keep it for now because we need to merge from budget -""" - -import copy -from bpfin.utilbills.bill import Bill -from bpfin.back_end_call.back_end_inputs import annual_bill -from bpfin.financials.financial_income import Income_Statement_Table -from bpfin.financials.financial_balance import Balance_Sheet_Table -from bpfin.financials.cash_balance import cash_balance -from bpfin.financials.liability import final_liability_dict -# from bpfin.financials.scenario import Scenario -from bpfin.financials.loan import Loan_List -from bpfin.lib.other import UTILITY_TYPE_LIST -from bpfin.financials.financial_budget_simulator import form_max_financing, form_budget_simulation_result -# from bpfin.tests.testdata import feature_data as db -# import pprint - -# Bill Overview Monthly Input -# def monthly_bill(raw_bill_table, analysis_date): -# """ -# Take in raw_bill_table -# Generate annual bill for utility type with available bill -# Generate manual_input dictionary indicating manual input label for 4 utilities -# Args: -# raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types -# analysis_date (dictionary): proforma's starting date and the years of proforma -# Returns: -# bill_overview_dict (dictionary): dictionary of dictionary of annual bills, see description for detail -# # manual_input_dict (dictionary): dictionary of boolean values, keys are utilities -# Error Validate: -# raw_bill_table, analysis_date == None, Raise empty error -# bill_overview_dict == None or missing utility, Raise calculation error -# manual_input_dict == None or missing utility, Raise calculation error -# Data Validate: -# raw_bill == None: no annual bill generated, manual_input == True -# raw_bill != None: -# raw_bill < 12 months, no annual bill generated, manual_input == True -# raw_bill > 12 months, annual bill generated, manual_input == False -# Description: -# raw_bill_table contains 4 utilities raw bill data. Each utility is a dictionary with 4 keys. -# Each utility can be None. See below for detail -# raw_bill_table = { -# 'electricity': { -# 'date_from': list of date, -# 'date_to', list of date, -# 'usage', list of float, -# 'charge', list of float -# }, -# 'gas': None, -# 'oil': None, -# 'water': None -# } -# bill_overview_dict = { -# 'electricity': {2014: 100, 2015:200, ...}, -# 'oil': dict of oil_bill, -# 'gas': dict of gas_bill, -# 'water': dict of water_bill} -# To Do: from utilbills.bill.py merge form_prior_month_bill(), and delete that one -# * this work is done. need all units test and then can delete that -# """ -# # if not raw_bill_table: -# # raise ValueError( -# # 'Bill_Overview - monthly_bill has empty raw_bill_table') -# if not analysis_date: -# raise ValueError('Bill_Overview - monthly_bill has empty analysis_date') -# bill_overview_dict = {} -# manual_input_dict = {} -# prior_month_bill_dict = {} -# for utility in UTILITY_TYPE_LIST: -# current_bill = Bill(utility, analysis_date) -# if utility in raw_bill_table: -# current_bill.put_month_bill(raw_bill_table[utility]) -# else: -# current_bill.put_month_bill(None) -# bill_overview_dict[utility] = current_bill.get_annual_bill() -# manual_input_dict[utility] = current_bill.get_manual_input() -# prior_month_bill_dict[utility] = current_bill.get_prior_proj_rough() -# if len(bill_overview_dict.keys()) != len(UTILITY_TYPE_LIST): -# raise ValueError( -# 'Bill_Overview - monthly_bill has incomplete result in bill_overview_dict' -# ) -# if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): -# raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') -# if len(prior_month_bill_dict.keys()) != len(UTILITY_TYPE_LIST): -# raise ValueError('Bill_Overview - monthly_bill has incomplete result of monthly_proj') -# return bill_overview_dict, manual_input_dict, prior_month_bill_dict -# # return bill_overview_dict - - -# def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): -# """ -# Take in raw_annual_bill_table -# Generate annual bill for utility type with and without available bill -# Args: -# raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types -# analysis_date (dictionary): proforma's starting date and the years of proforma -# raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required -# Returns: -# annual_bill_table (dictionary): dictionary of dictionary of annual bills, see description for detail -# Description: -# raw_annual_bill_table = { -# 'electricity': {2014: 100, 2015:200, ...}, -# 'oil': dict of oil_bill, -# 'gas': dict of gas_bill, -# 'water': dict of water_bill} -# annual_bill_table = { -# 'electricity': {2014: 100, 2015:200, ...}, -# 'oil': dict of oil_bill, -# 'gas': dict of gas_bill, -# 'water': dict of water_bill} -# """ -# # if not raw_bill_table: -# # raise ValueError( -# # 'Bill_Overview - annual_bill has empty raw_bill_table') -# # if not raw_annual_bill_table: -# # raise ValueError( -# # 'Bill_Overview - annual_bill has empty raw_annual_bill_table') -# if not analysis_date: -# raise ValueError('Bill_Overview - annual_bill has empty analysis_date') - -# annual_bill_table = {} -# manual_input_dict = {} -# for utility in UTILITY_TYPE_LIST: -# current_bill = Bill(utility, analysis_date) -# if utility in raw_bill_table: -# current_bill.put_month_bill(raw_bill_table[utility]) -# else: -# current_bill.put_month_bill(None) -# if utility in raw_annual_bill_table: -# current_bill.put_annual_bill(raw_annual_bill_table[utility]) -# annual_bill_table[utility] = current_bill.get_annual_bill() -# manual_input_dict[utility] = current_bill.get_manual_input() -# if len(annual_bill_table.keys()) != len(UTILITY_TYPE_LIST): -# raise ValueError( -# 'Bill_Overview - annual_bill has incomplete result in annual_bill_table' -# ) -# if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): -# raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') -# return annual_bill_table, manual_input_dict -# # return annual_bill_table - - -# def form_prior_income_table( -# raw_income_input, -# raw_bill_table, -# raw_annual_bill_table, -# analysis_date, -# growth_rate_flag): -# """ -# Take in prior-annual_bill_table, and raw inputs of income statements -# Generate historical income statement, calculate its characters including CAGR and other percent ratios -# Generate prior_saving income statement. Store characters in database -# Args: -# raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year -# raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types -# raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required -# analysis_date (dictionary): proforma's starting date and the years of proforma -# growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average -# Returns: -# prior_income (dictionary): dict of dict of prior_saving income statement. Key is year -# next_year_income (dictionary): next year income statement. Keys are income statement items. -# average_income (dictionary): single year income statement for historical average. Keys = income statement items -# historical_cagr (float): compound annual growth rate -# Description: -# raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} -# raw_bill_table = { -# 'electricity': { -# 'date_from': list of date, -# 'date_to', list of date, -# 'usage', list of float, -# 'charge', list of float -# }, -# 'gas': None, -# 'oil': None, -# 'water': None -# } -# raw_annual_bill_table = { -# 'electricity': None -# 'gas': {2014: 100, 2015:200, ...}, -# 'oil': {2014: 100, 2015:200, ...}, -# 'water': dict of water_bill} -# prior_income = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} -# """ -# annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date)[0] -# income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) -# income_table.project(growth_rate_flag, annual_bill_table) - -# prior_income = income_table.get_full_income_table() -# next_year_income = income_table.get_single_year(income_table.hist_end_year + 1) -# average_income = income_table.get_average() -# historical_cagr = income_table.get_cagr() -# return prior_income, next_year_income, average_income, historical_cagr - - -# def prelim_scenario(raw_bill_table, raw_annual_bill_table, -# raw_income_input, growth_rate_flag, -# raw_liability_input, raw_cash_balance, -# raw_loan_input_list, -# analysis_date, commission_date, construction_cost, -# percent_saving_dict, full_saving_dict, -# req_dscr, customer_preference): -# from bpfin.financials.scenario import Scenario -# prior_annual_bill_table, manual_input_dict = annual_bill( -# raw_bill_table, raw_annual_bill_table, analysis_date -# ) - -# prior_income_table = Income_Statement_Table(raw_income_input, prior_annual_bill_table, analysis_date) - -# cash_balance_dict = cash_balance(analysis_date, raw_cash_balance) -# liability_dict = final_liability_dict(analysis_date, raw_liability_input) - -# balance_sheet_input = { -# 'cash_dictionary': cash_balance_dict, -# 'other_debt_service_dictionary': liability_dict, -# 'net_income_dictionary': prior_income_table.get_noi_dict() -# } - -# prior_income_table.project(growth_rate_flag, prior_annual_bill_table) - -# prior_bs = Balance_Sheet_Table(cash_balance_dict, liability_dict, prior_income_table.get_noi_dict()) - -# prior_bs.project_balance_sheet(analysis_date, liability_dict, prior_income_table.get_noi_dict()) - -# scenario_ob = Scenario( -# analysis_date=analysis_date, -# commission_date=commission_date, -# construction_cost=construction_cost, -# manual_input_dict=manual_input_dict, -# prior_annual_bill_table=prior_annual_bill_table, -# prior_income_statement_table=prior_income_table, -# other_debt_service=liability_dict, -# growth_rate_flag=growth_rate_flag, -# prior_balance_sheet_table=prior_bs, -# loan_input_list=raw_loan_input_list) - -# scenario_ob.prelim_analysis( -# prior_month_bill=monthly_bill(raw_bill_table, analysis_date)[2], -# percent_saving_dict=percent_saving_dict, -# full_saving_dict=full_saving_dict, -# growth_rate_flag=growth_rate_flag, -# req_dscr=req_dscr, -# customer_preference=customer_preference) - -# # Define raw_annual_bill_table as a "get" of prior_bill -# # Define raw_bill_table as a "get" of prior_bill -# # Delete prior_annual_bill_table - -# return scenario_ob.get_graph_dict() - - -def budget_simulation( - analysis_date, - commission_date, - customer_preference, - req_dscr, - raw_bill_table, - raw_annual_bill_table, - raw_income_input, - growth_rate_flag, - raw_liability_input, - raw_cash_balance, - raw_loan_input_list): - """ - Generate budget simulation data, to draw graph and to put in table - To Do: - Need to refactor when merge from saving-scenario refactoring - Need to validate data, to allow a lot of empty inputs - Write test file - saving_interval will be read from frond-end in next version - """ - preference_list = ['loan_only', 'loan_first', 'sf_first', 'sf_max'] - - annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date)[0] - - income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) - income_table.project(growth_rate_flag, annual_bill_table) - - cash_dict = cash_balance(analysis_date, raw_cash_balance) - liability_dict = final_liability_dict(analysis_date, raw_liability_input) - balance_sheet = Balance_Sheet_Table(cash_dict, liability_dict, income_table.get_noi_dict()) - balance_sheet.project_balance_sheet(analysis_date, liability_dict, income_table.get_noi_dict()) - - loan_list = Loan_List(raw_loan_input_list).get_loan_list() - - first_year_energy = income_table.get_total_energy_dict()[commission_date.year + 1] - - first_year_cash = balance_sheet.get_first_year_cash(commission_date) - first_year_noi = income_table.get_first_year_noi(commission_date) - - saving_interval = 0.1000 - saving_potential_list = [] - interval = saving_interval - i = 0 - while interval <= 0.5: - i += 1 - saving_potential_list.append(interval) - interval += saving_interval - if i >= 9999: - break - - budget_simulation_result = form_budget_simulation_result(loan_list) - - for saving_percent in saving_potential_list: - financing_per_save = form_max_financing( - loan_list=loan_list, - downpayment_max=customer_preference['downpayment_max'], - expected_payback=customer_preference['expected_payback'], - req_dscr=req_dscr, - first_year_noi=first_year_noi, - first_year_cash=first_year_cash, - savings=first_year_energy * saving_percent - ) - for preference in preference_list: - amount_list = copy.deepcopy(financing_per_save[preference]) - amount_list.insert(0, saving_percent) - for current_list, amount in zip(budget_simulation_result[preference], amount_list): - current_list.append(amount) - return budget_simulation_result - - -# **** ugly test **** -# print('\nmonthly_bill =', monthly_bill(db.raw_bill_table, db.analysis_date)) -# print('\nannual_bill =', annual_bill(db.raw_bill_table, db.raw_annual_bill_table, db.analysis_date)) -# print('\nprior_income_statement =', prior_income_statement_table( -# db.raw_income_input, -# db.raw_bill_table, -# db.raw_annual_bill_table, -# db.analysis_date, -# -2.0)) - -# result = prior_income_statement_table( -# db.raw_income_input, -# db.raw_bill_table, -# db.raw_annual_bill_table, -# db.analysis_date, -# -2.0) - -# writein = str(result) -# f = open('data_generation.py', 'w') -# f.write(writein) -# f.close() - -# finalresult = budget_simulation( -# db.analysis_date, -# db.commission_date, -# db.customer_preference, -# db.req_dscr, -# db.raw_bill_table, -# db.raw_annual_bill_table, -# db.raw_income_input, -# -2.0, -# db.raw_liability_input, -# db.raw_cash_balance, -# db.raw_loan_input_list -# ) - - -# import pprint -# pp = pprint.PrettyPrinter(width=120, indent=4, compact=True) -# pp.pprint(finalresult) - - - -# **** old work **** -# # Bill Overview Monthly Input -# def monthly_bill(raw_bill_table, analysis_date): -# """ -# Take in raw_bill_table -# Generate annual bill for utility type with available bill -# Generate manual_input dictionary indicating manual input label for 4 utilities - -# Args: -# raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types -# analysis_date (dictionary): proforma's starting date and the years of proforma - -# Returns: -# bill_overview_dict (dictionary): dictionary of dictionary of annual bills, see description for detail -# # manual_input_dict (dictionary): dictionary of boolean values, keys are utilities - -# Error Validate: -# raw_bill_table, analysis_date == None, Raise empty error -# bill_overview_dict == None or missing utility, Raise calculation error -# manual_input_dict == None or missing utility, Raise calculation error -# Data Validate: -# raw_bill == None: no annual bill generated, manual_input == True -# raw_bill != None: -# raw_bill < 12 months, no annual bill generated, manual_input == True -# raw_bill > 12 months, annual bill generated, manual_input == False - -# Description: -# raw_bill_table contains 4 utilities raw bill data. Each utility is a dictionary with 4 keys. -# Each utility can be None. See below for detail -# raw_bill_table = { -# 'electricity': { -# 'date_from': list of date, -# 'date_to', list of date, -# 'usage', list of float, -# 'charge', list of float -# }, -# 'gas': None, -# 'oil': None, -# 'water': None -# } - -# bill_overview_dict = { -# 'electricity': {2014: 100, 2015:200, ...}, -# 'oil': dict of oil_bill, -# 'gas': dict of gas_bill, -# 'water': dict of water_bill} -# """ -# if not raw_bill_table: -# raise ValueError('Bill_Overview - monthly_bill has empty raw_bill_table') -# if not analysis_date: -# raise ValueError('Bill_Overview - monthly_bill has empty analysis_date') -# bill_overview_dict = {} -# manual_input_dict = {} -# for utility in UTILITY_TYPE_LIST: -# current_bill = Bill(utility, analysis_date) -# if utility in raw_bill_table: -# current_bill.put_month_bill(raw_bill_table[utility]) -# else: -# current_bill.put_month_bill(None) -# bill_overview_dict[utility] = current_bill.get_annual_bill() -# manual_input_dict[utility] = current_bill.get_manual_input() -# if len(bill_overview_dict.keys()) != len(UTILITY_TYPE_LIST): -# raise ValueError('Bill_Overview - monthly_bill has incomplete result in bill_overview_dict') -# # if len(manual_input_dict.keys()) != len(UTILITY_TYPE_LIST): -# # raise ValueError('Bill_Overview - monthly_bill has incomplete result in manual_input_dict') -# # return bill_overview_dict, manual_input_dict -# return bill_overview_dict - - -# def annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date): -# """ -# Take in raw_annual_bill_table -# Generate annual bill for utility type with and without available bill - -# Args: -# raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types -# analysis_date (dictionary): proforma's starting date and the years of proforma -# raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required - -# Returns: -# annual_bill_table (dictionary): dictionary of dictionary of annual bills, see description for detail - -# Description: -# raw_annual_bill_table = { -# 'electricity': {2014: 100, 2015:200, ...}, -# 'oil': dict of oil_bill, -# 'gas': dict of gas_bill, -# 'water': dict of water_bill} -# annual_bill_table = { -# 'electricity': {2014: 100, 2015:200, ...}, -# 'oil': dict of oil_bill, -# 'gas': dict of gas_bill, -# 'water': dict of water_bill} -# """ -# if not raw_bill_table: -# raise ValueError('Bill_Overview - annual_bill has empty raw_bill_table') -# if not raw_annual_bill_table: -# raise ValueError('Bill_Overview - annual_bill has empty raw_annual_bill_table') -# if not analysis_date: -# raise ValueError('Bill_Overview - annual_bill has empty analysis_date') - -# annual_bill_table = {} -# for utility in UTILITY_TYPE_LIST: -# current_bill = Bill(utility, analysis_date) -# if utility in raw_bill_table: -# current_bill.put_month_bill(raw_bill_table[utility]) -# else: -# current_bill.put_month_bill(None) -# if utility in raw_annual_bill_table: -# current_bill.put_annual_bill(raw_annual_bill_table[utility]) -# annual_bill_table[utility] = current_bill.get_annual_bill() -# if len(annual_bill_table.keys()) != len(UTILITY_TYPE_LIST): -# raise ValueError('Bill_Overview - annual_bill has incomplete result in annual_bill_table') -# return annual_bill_table - - -# def prior_income_statement_table( -# raw_income_input, -# raw_bill_table, -# raw_annual_bill_table, -# analysis_date, -# growth_rate_flag): -# """ -# Take in prior-annual_bill_table, and raw inputs of income statements -# Generate historical income statement, calculate its characters including CAGR and other percent ratios -# Generate prior_saving income statement. Store characters in database - -# Args: -# raw_income_input (dictionary): dict of dict of incomplete income statement, for historical years. Key = year -# raw_bill_table (dictionary): dict of dict of raw_bill. Keys are utility types -# raw_annual_bill_table (dictionary): dictionary of dictionary of annual bills, at lease 0 year data is required -# analysis_date (dictionary): proforma's starting date and the years of proforma -# growth_rate_flag (float): indicating assumed growth rate, -2.0 == cagr, -1.0 == historical average - -# Returns: -# prior_income (dictionary): dict of dict of prior_saving income statement. Key is year -# next_year_income (dictionary): next year income statement. Keys are income statement items. -# average_income (dictionary): single year income statement for historical average. Keys = income statement items -# historical_cagr (float): compound annual growth rate - -# Description: -# raw_income_input = {2014: {'revenue': 90.0, 'utility_expense': 55.0, 'non_utility_expense': 35.0}, 2015:{},} - -# raw_bill_table = { -# 'electricity': { -# 'date_from': list of date, -# 'date_to', list of date, -# 'usage', list of float, -# 'charge', list of float -# }, -# 'gas': None, -# 'oil': None, -# 'water': None -# } - -# raw_annual_bill_table = { -# 'electricity': None -# 'gas': {2014: 100, 2015:200, ...}, -# 'oil': {2014: 100, 2015:200, ...}, -# 'water': dict of water_bill} - -# prior_income_statement = {2014: {'year': 2014, 'revenue': 100.0, ..., 'not': 5.0}, ... , 2016:{}} - -# """ -# annual_bill_table = annual_bill(raw_bill_table, raw_annual_bill_table, analysis_date) -# income_table = Income_Statement_Table(raw_income_input, annual_bill_table, analysis_date) -# income_table.project(growth_rate_flag, annual_bill_table) - -# prior_income = income_table.get_full_income_table() -# next_year_income = income_table.get_single_year(income_table.hist_end_year + 1) -# average_income = income_table.get_average() -# historical_cagr = income_table.get_cagr() -# return prior_income, next_year_income, average_income, historical_cagr - diff --git a/bpfin/tests/test_back_end_call/test_back_end_budget.py b/bpfin/tests/test_back_end_call/test_back_end_budget.py index a143bbe..d05d722 100644 --- a/bpfin/tests/test_back_end_call/test_back_end_budget.py +++ b/bpfin/tests/test_back_end_call/test_back_end_budget.py @@ -1,4 +1,4 @@ -from bpfin.lib.back_end_call import budget_simulation +from bpfin.back_end_call.back_end_budget import budget_simulation from bpfin.tests.testdata import feature_data as db diff --git a/bpfin/tests/test_back_end_call/test_back_end_inputs.py b/bpfin/tests/test_back_end_call/test_back_end_inputs.py index 90c20b6..ebb9801 100644 --- a/bpfin/tests/test_back_end_call/test_back_end_inputs.py +++ b/bpfin/tests/test_back_end_call/test_back_end_inputs.py @@ -1,6 +1,4 @@ -# from bpfin.lib.back_end_call import monthly_bill, annual_bill, form_prior_income_table -from bpfin.lib.back_end_call import budget_simulation -# from bpfin.tests.testdata import sample_data as sdb +# from bpfin.lib.back_end_call import budget_simulation from bpfin.back_end_call.back_end_inputs import monthly_bill, annual_bill, form_prior_income_table from bpfin.tests.testdata import feature_data as db @@ -38,19 +36,20 @@ def test_form_prior_income_table(): assert output_historical_cagr == historical_cagr -def test_budget_simulation(): - output = db.budget_simulation_result - result = budget_simulation( - db.analysis_date, - db.commission_date, - db.customer_preference, - db.req_dscr, - db.raw_bill_table, - db.raw_annual_bill_table, - db.raw_income_input, - db.growth_rate_flag, - db.raw_liability_input, - db.raw_cash_balance, - db.raw_loan_input_list - ) - assert output == result +# should be tested in other test file +# def test_budget_simulation(): +# output = db.budget_simulation_result +# result = budget_simulation( +# db.analysis_date, +# db.commission_date, +# db.customer_preference, +# db.req_dscr, +# db.raw_bill_table, +# db.raw_annual_bill_table, +# db.raw_income_input, +# db.growth_rate_flag, +# db.raw_liability_input, +# db.raw_cash_balance, +# db.raw_loan_input_list +# ) +# assert output == result diff --git a/bpfin/tests/test_lib/test_back_end_call.py b/bpfin/tests/test_lib/test_back_end_call.py deleted file mode 100644 index 148f5aa..0000000 --- a/bpfin/tests/test_lib/test_back_end_call.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -This file should get removed after budget merged -""" -- GitLab From d8264f3751ef6212cdca682f677b7df9213bdd6e Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 2 Jun 2017 22:01:44 -0400 Subject: [PATCH 15/17] Get ready for merging --- bpfin/back_end_call/back_end_full.py | 3 +++ bpfin/tests/test_back_end_call/test_back_end_prelim.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/bpfin/back_end_call/back_end_full.py b/bpfin/back_end_call/back_end_full.py index e69de29..b291161 100644 --- a/bpfin/back_end_call/back_end_full.py +++ b/bpfin/back_end_call/back_end_full.py @@ -0,0 +1,3 @@ +""" +Back_end functions. Will develop when full analysis is created +""" diff --git a/bpfin/tests/test_back_end_call/test_back_end_prelim.py b/bpfin/tests/test_back_end_call/test_back_end_prelim.py index e69de29..9be90b1 100644 --- a/bpfin/tests/test_back_end_call/test_back_end_prelim.py +++ b/bpfin/tests/test_back_end_call/test_back_end_prelim.py @@ -0,0 +1,3 @@ +""" +To Do we still need to write test files for back_end_call prelim_analysis +""" -- GitLab From a11f9356a3aff3169628449ae17ed24218a7d260 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Fri, 2 Jun 2017 22:16:59 -0400 Subject: [PATCH 16/17] Clean test data --- .../test_back_end_inputs.py | 19 ---------- bpfin/tests/testdata/feature_data.py | 38 +++++-------------- 2 files changed, 10 insertions(+), 47 deletions(-) diff --git a/bpfin/tests/test_back_end_call/test_back_end_inputs.py b/bpfin/tests/test_back_end_call/test_back_end_inputs.py index ebb9801..8033219 100644 --- a/bpfin/tests/test_back_end_call/test_back_end_inputs.py +++ b/bpfin/tests/test_back_end_call/test_back_end_inputs.py @@ -34,22 +34,3 @@ def test_form_prior_income_table(): assert output_next_year_income == next_year_income assert output_average_income == average_income assert output_historical_cagr == historical_cagr - - -# should be tested in other test file -# def test_budget_simulation(): -# output = db.budget_simulation_result -# result = budget_simulation( -# db.analysis_date, -# db.commission_date, -# db.customer_preference, -# db.req_dscr, -# db.raw_bill_table, -# db.raw_annual_bill_table, -# db.raw_income_input, -# db.growth_rate_flag, -# db.raw_liability_input, -# db.raw_cash_balance, -# db.raw_loan_input_list -# ) -# assert output == result diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 6d97b57..2327a6c 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -166,20 +166,6 @@ raw_water_bill_demo['charge'] = [ raw_water_bill_demo['usage'] = [ 3500000, 3600000, 3300000] -# ======= - -# raw_water_bill_demo = {} -# raw_water_bill_demo['utility_type'] = 'water' -# raw_water_bill_demo['date_from'] = [ -# date(2016, 1, 3), date(2015, 1, 1), date(2014, 1, 2)] -# raw_water_bill_demo['date_to'] = [ -# date(2016, 12, 28), date(2016, 1, 2), date(2014, 12, 31)] -# raw_water_bill_demo['charge'] = [ -# 20000, 20500, 21000] -# raw_water_bill_demo['usage'] = [ -# 3500000, 3600000, 3300000] - -# >>>>>>> 93e0d3f2b2699a8ba08498bd19a0f2288ff25672 raw_bill_table = { 'gas': raw_gas_bill_demo, 'electricity': raw_elec_bill_demo, @@ -703,8 +689,15 @@ average_income = { historical_cagr = 0.01242283656582921 growth_rate_flag = -2.0 -budget_simulation_result = { - 'loan_first': +budget_simulation_result = ( + [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ + 'budget', 37723.783022506694, 68031.962268808478, 68031.962268808478, + 68031.962268808478, 68031.962268808478 + ], ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0], + ['Joe Fund', 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], [ + 'Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, + 18031.962268808486, 18031.962268808486 + ], ['self_finance', 0.0, 0.0, 0.0, 0.0, 0.0]], [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ 'budget', 37723.783022506701, 77213.635473036207, 118031.96226880848, 118031.96226880848, 118031.96226880848 @@ -716,16 +709,6 @@ budget_simulation_result = { 'self_finance', 9.0949470177292824e-12, 9181.6732042277363, 50000.0, 50000.0, 50000.0 ]], - 'loan_only': - [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ - 'budget', 37723.783022506694, 68031.962268808478, 68031.962268808478, - 68031.962268808478, 68031.962268808478 - ], ['NYSERDA', 0.0, 0.0, 0.0, 0.0, 0.0], - ['Joe Fund', 37723.783022506694, 50000.0, 50000.0, 50000.0, 50000.0], [ - 'Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, - 18031.962268808486, 18031.962268808486 - ], ['self_finance', 0.0, 0.0, 0.0, 0.0, 0.0]], - 'sf_first': [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ 'budget', 52184.67464428641, 89908.457666793111, 118031.96226880848, 118031.96226880848, 118031.96226880848 @@ -736,7 +719,6 @@ budget_simulation_result = { 'Tooraj Capital', 0.0, 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486 ], ['self_finance', 50000.0, 50000.0, 50000.0, 50000.0, 50000.0]], - 'sf_max': [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ 'budget', 87723.783022506686, 118031.96226880848, 118031.96226880848, 118031.96226880848, 118031.96226880848 @@ -746,7 +728,7 @@ budget_simulation_result = { 'Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486, 18031.962268808486 ], ['self_finance', 50000.0, 50000.0, 50000.0, 50000.0, 50000.0]] -} +) post_annual_bill = { 'electricity': { -- GitLab From b68e596dd30f1c0a58f795b7b2a79a6093655b15 Mon Sep 17 00:00:00 2001 From: chenzheng06 Date: Sat, 3 Jun 2017 23:06:57 -0400 Subject: [PATCH 17/17] Modify as coments --- bpfin/back_end_call/back_end_budget.py | 4 ++-- bpfin/tests/testdata/feature_data.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bpfin/back_end_call/back_end_budget.py b/bpfin/back_end_call/back_end_budget.py index e4dda8d..81d3fd6 100644 --- a/bpfin/back_end_call/back_end_budget.py +++ b/bpfin/back_end_call/back_end_budget.py @@ -76,11 +76,11 @@ def budget_simulation( for current_list, amount in zip(budget_simulation_result[preference], amount_list): current_list.append(amount) - return ( + return [ budget_simulation_result['loan_only'], budget_simulation_result['loan_first'], budget_simulation_result['sf_first'], - budget_simulation_result['sf_max']) + budget_simulation_result['sf_max']] # **** ugly test **** diff --git a/bpfin/tests/testdata/feature_data.py b/bpfin/tests/testdata/feature_data.py index 2327a6c..8b37a7a 100644 --- a/bpfin/tests/testdata/feature_data.py +++ b/bpfin/tests/testdata/feature_data.py @@ -689,7 +689,7 @@ average_income = { historical_cagr = 0.01242283656582921 growth_rate_flag = -2.0 -budget_simulation_result = ( +budget_simulation_result = [ [['saving_percentage', 0.1, 0.2, 0.30000000000000004, 0.4, 0.5], [ 'budget', 37723.783022506694, 68031.962268808478, 68031.962268808478, 68031.962268808478, 68031.962268808478 @@ -728,7 +728,7 @@ budget_simulation_result = ( 'Tooraj Capital', 0.0, 18031.962268808486, 18031.962268808486, 18031.962268808486, 18031.962268808486 ], ['self_finance', 50000.0, 50000.0, 50000.0, 50000.0, 50000.0]] -) +] post_annual_bill = { 'electricity': { -- GitLab