diff --git a/bpfin/financials/financial_lib.py b/bpfin/financials/financial_lib.py index 3b213e29f56c6bda8ecf9a9d3bbd3a54fb0e2dc9..a72c3c107e1a7616bc05e0a1d9707564f606bdb7 100644 --- a/bpfin/financials/financial_lib.py +++ b/bpfin/financials/financial_lib.py @@ -1,25 +1,25 @@ # import datetime # import calendar # from bpfin.lib import other as lib +import bpfin.utilbills.bill_lib as bl +from bpfin.tests.testdata import sample_data as db # import pprint -# calculate single year income statement given inputs from UI -def income_statement_single_year(income_input, bill_overview): - '''calculate income statement for 1 year, with inputs from UI and energy bill overview +def income_statement_single_year(year, income_input, bill_overview): + '''calculate income statement for one single year, with inputs from UI and energy bill overview Args: + year (int): the year of this income statement income_input (dictionary): 4 elements of inputs - bill_overview (dictionary): contains annual energy charge for 4 utility types + bill_overview (dictionary): bill_overview from UI, with blank cells, for 4 utility_types Returns: dictionary: income statement with standarized items Description Sample: - income_input = {'year': 2016, 'revenue': 100000, 'utility_expense': 60000,'non_utility_expense': 3000} + income_input = {revenue': 100000, 'utility_expense': 60000,'non_utility_expense': 3000} bill_overview = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} ''' - - year = income_input['year'] revenue = income_input['revenue'] utility_expense = income_input['utility_expense'] non_utility_expense = income_input['non_utility_expense'] @@ -46,39 +46,117 @@ def income_statement_single_year(income_input, bill_overview): 'revenue': revenue, 'utility_expense': utility_expense, 'energy_opex': energy_opex, - 'electricity_opex': electricity_opex, - 'gas_opex': gas_opex, - 'oil_opex': oil_opex, + 'electricity_opex': electricity_opex, + 'gas_opex': gas_opex, + 'oil_opex': oil_opex, 'water_opex': water_opex, 'other_utility': other_utility, - 'non_utility_expense': non_utility_expense, + 'non_utility_expense': non_utility_expense, 'net_non_energy_opex': net_non_energy_opex, 'total_opex': total_opex, - 'noi': noi} - - # print(income_statement) + 'noi': noi + } return income_statement -# income_input_2016 = { -# 'year': 2016, -# 'revenue': 100000, -# 'utility_expense': 60000, -# 'non_utility_expense': 3000 -# } - -# electricity_bill = { -# 2012: 32220.590596217822, 2013: 32782.518348206999, 2014: 33293.100530249467, 2015: 33546.761580451312, -# 2016: 33844.501703633454, 2017: 34523.27291517199, 2018: 35384.966283060414, 2019: 36251.490761516034, -# 2020: 37100.197369238413, 2021: 37887.082013632862, 2022: 38603.765037606507, 2023: 39307.925216552088, -# 2024: 40028.077679374117, 2025: 40772.42926238777, 2026: 41575.666985412594, 2027: 42439.918590730398, -# 2028: 43325.235463581725, 2029: 44211.298795796341, 2030: 45097.667796776521, 2031: 45986.665329650692, -# 2032: 46886.305428683903, 2033: 47806.578908870637, 2034: 48751.551094031129, 2035: 49722.255450564422, -# 2036: 50718.832984188753} -# oil_bill = {2015: 1010, 2016: 1210, 2017: 1510} -# gas_bill = {2015: 1020, 2016: 1220, 2017: 1520} -# water_bill = {2015: 0, 2016: 0, 2017: 0} - -# bill_overview = {'electricity': electricity_bill, 'oil': oil_bill, 'gas': gas_bill, 'water': water_bill} - -# income_statement_single_year(income_input_2016, bill_overview) +def organize_bill_overview(bill_overview, analysis_date): + '''take bill_overview as inputs, fill in the blank annual bill with average numbers + + Args: + bill_overview (dictionary): bill_overview from UI, with blank cells, for 4 utility_types + analysis_date (dictionary): proforma's starting date and the years of proforma + Returns: + dictionary: bill_overview_organized, annual bills for 4 utility types, with blank cells filled + + Description: + analysis_date: {'proforma_start': datetime.date, 'proforma_duration': int} + ''' + proforma_year = bl.form_bill_year(analysis_date['proforma_start'], + analysis_date['proforma_duration']) + bill_dict = {} + bill_dict[1] = bill_overview['electricity'] # electricity_bill + bill_dict[2] = bill_overview['gas'] # gas_bill + bill_dict[3] = bill_overview['oil'] # oil_bill + bill_dict[4] = bill_overview['water'] # water_bill + average_bill_dict = {} + + for i in range(4): + if bill_dict[i + 1][1] is False: + average_bill_dict[i + 1] = float( + sum(bill_dict[i + 1][0][year] for year in bill_dict[i + 1][ + 0])) / len(bill_dict[i + 1][0]) + + for year in proforma_year: + for i in range(4): + if year in bill_dict[i + 1][0]: + pass + else: + bill_dict[i + 1][0][year] = average_bill_dict[i + 1] + bill_overview_organized = { + 'electricity': bill_dict[1][0], + 'gas': bill_dict[2][0], + 'oil': bill_dict[3][0], + 'water': bill_dict[4][0] + } + return bill_overview_organized + + +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 + + def assign_next(self, income_statement_characters, bill_overview_organized): + 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 assign_next_year(self, other_utility_percent, non_utility_expense_percent): + # # print('\n other_utility_percent',other_utility_percent) + # # print('\n non_utility_expense_percent',non_utility_expense_percent) + # self.energy_opex = self.electricity_opex + self.gas_opex + self.oil_opex + self.water_opex + # # print('\n',self.energy_opex) + # # print('\n revenue', self.revenue) + # self.other_utility = self.revenue * other_utility_percent + # self.non_utility_expense = self.revenue * 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): + 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 diff --git a/bpfin/financials/income_statement_form_hist.py b/bpfin/financials/income_statement_form_hist.py new file mode 100644 index 0000000000000000000000000000000000000000..019ecc5a4af33f6507a78875941f5c8c5c161840 --- /dev/null +++ b/bpfin/financials/income_statement_form_hist.py @@ -0,0 +1,61 @@ +import bpfin.financials.financial_lib as fl +from bpfin.lib import other as lib + + +def income_statement_character(income_statement_hist): + '''Determine annual growth rate, other_utility_percentage, non_utility_expense_percentage + Args: + income_statement_hist (dictionary): full income statement, with all items filled, for available years + Return: + dictionary: three characters for future projection + + Description: + income_statement_hist = {2014:{'revenue': 100000, ... ,'noi':37000}, 2015:{}, 2016:{}} + output = {'cagr': 2.45, 'other_utility_percentage': 20.4%, 'non_utility_expense_percentage': 43.5%, ..} + ''' + sorted_income_hist_year = sorted(income_statement_hist) + start_year = sorted_income_hist_year[0] + end_year = sorted_income_hist_year[-1] + # if start_year == None: return None + cagr = lib.cal_cagr( + income_statement_hist[start_year]['revenue'], + income_statement_hist[end_year]['revenue'], + end_year - start_year) + revenue_sum = 0 + other_utility_sum = 0 + non_utility_expense_sum = 0 + revenue_average = 0 + for year in sorted_income_hist_year: + revenue_sum += income_statement_hist[year]['revenue'] + other_utility_sum += income_statement_hist[year]['other_utility'] + non_utility_expense_sum += income_statement_hist[year]['non_utility_expense'] + result_dict = { + 'start_year': start_year, + 'end_year': end_year, + 'cagr': cagr, + 'other_utility_percent': other_utility_sum/revenue_sum, + 'non_utility_expense_percent': non_utility_expense_sum/revenue_sum, + 'revenue_average': revenue_sum / (end_year - start_year + 1) + } + return result_dict + + +def form_income_statement_hist(raw_income_input, bill_overview_organized, analysis_date): + ''' form income statement table with raw inputs from UI, and organized bill_overview. NO projection + Args: + raw_income_input (dictionary): dictionary of dictionary. raw inputs for income statement for available years + bill_overview_organized (dictionary): dict of dict, 4 utility types, with blank charge filled with average + analysis_date (dictionary): proforma's starting date and the years of proforma + Returns: + Dictionary: dict of dict, full income statement for available years + + Description: + raw_income_input = {2014: {'revenue': 90000, 'utility_expense': 55000, 'non_utility_expense': 3500}, 2015:{},} + bill_overview_organized = {'electricity':{2012: 100, 2013: 120, ...}, 'gas':{2012:100,2013:130...},...} + Output = {2014: {'year': 2014, 'revenue': ...}, 2015:{...}, 2016:{...}} + ''' + income_statement_full = {} + for year in sorted(raw_income_input): + income_statement_full[year] = fl.income_statement_single_year( + year, raw_income_input[year], bill_overview_organized) + return income_statement_full diff --git a/bpfin/financials/income_statement_next.py b/bpfin/financials/income_statement_next.py new file mode 100644 index 0000000000000000000000000000000000000000..60b42bcb1302ef3775cfdef037a52f78f653c5db --- /dev/null +++ b/bpfin/financials/income_statement_next.py @@ -0,0 +1,68 @@ +import bpfin.financials.financial_lib as fl +import bpfin.tests.testdata.sample_data as db +import bpfin.utilbills.bill_lib as bl +from bpfin.lib import other as lib +from bpfin.financials.income_statement_form_hist import income_statement_character + + +def income_statement_next(income_statement_hist, bill_overview_organized, growth_rate_flag): + '''Project income statement for UI input. inputs are whatever bill and financial statements are available + Args: + income_statement_hist (dictionary): historical income statement, with all items filled, for available years + bill_overview_organized (dictionary): annual bills for 4 utility types, with blank cells filled + 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: + dictionary: 1 year full income statement, with itmes calculated. + ''' + characters = income_statement_character(income_statement_hist) + + income_next = fl.Income_Statement() + income_next.year = characters['end_year'] + 1 + + if growth_rate_flag == -1.0: + income_next.revenue = characters['revenue_average'] + else: + if growth_rate_flag == -2.0: + growth_rate = characters['cagr'] + income_next.revenue = income_statement_hist[characters['end_year']]['revenue'] * (1 + growth_rate) + else: + growth_rate = growth_rate_flag # 0.00, 0.01, 0.02 ... + income_next.revenue = income_statement_hist[characters['end_year']]['revenue'] * (1 + growth_rate) + + income_next.assign_next(characters, bill_overview_organized) + result_dict = fl.convert_income_statement_class(income_next) + print(result_dict) + return result_dict + +# def income_statement_proj_input(income_statement_full, bill_overview_organized, growth_rate_flag): +# '''Project income statement for UI input. inputs are whatever bill and financial statements are available +# Args: +# income_statement_full (dictionary) +# bill_overview_organized (dictionary): bill_overview from UI, with blank cells, for 4 utility_types +# analysis_date (dictionary): proforma's starting date and the years of proforma +# growth_rate_flag (float) +# Returns: +# dictionary: +# ''' +# # proforma_year = bl.form_bill_year(analysis_date['proforma_start'], analysis_date['proforma_duration']) +# characters = income_statement_character(income_statement_full) +# cagr = characters['cagr'] +# other_utility_percent = characters['other_utility_percent'] +# non_utility_expense_percent = characters['non_utility_expense_percent'] +# growth_rate = cagr # !!!! need logic for growth_rate_flag +# end_year = sorted(income_statement_full)[-1] +# income_next = fl.Income_Statement() +# income_next.year = end_year + 1 +# income_next.revenue = income_statement_full[end_year]['revenue'] * (1 + growth_rate) +# income_next.electricity_opex = bill_overview_organized['electricity'][end_year + 1] +# income_next.gas_opex = bill_overview_organized['gas'][end_year + 1] +# income_next.oil_opex = bill_overview_organized['oil'][end_year + 1] +# income_next.water_opex = bill_overview_organized['water'][end_year + 1] +# income_next.assign_next_year(other_utility_percent, non_utility_expense_percent) +# result_dict = fl.convert_income_statement_class(income_next) +# return result_dict + +# income statements should be availabe for at least 1 year. If comes with >1 years, suppose to be continued. +# income_statement_proj_input(db.income_statement_full, db.bill_overview_organized, 0.01) +# income_next(2017, db.income_statement_full, db.bill_overview_organized, -2.0) diff --git a/bpfin/lib/other.py b/bpfin/lib/other.py index 63cdf26b48fc8f08cfaf6c89cece526cfd7845ea..846a52264a58a0b0752a3adc18065da84d694357 100644 --- a/bpfin/lib/other.py +++ b/bpfin/lib/other.py @@ -316,12 +316,20 @@ def average(list1): return sum(list1) / len(list1) -def cal_CAGR(start_value, end_value, n): +def cal_cagr(start_value, end_value, n): + ''' Calculate compound anual growth rate + Args: + start_value (float): first year value + nd_value (float): last year value + n (int): first year - last year + Return: + float: anual growth rate + ''' if start_value != 0: - CAGR = (end_value / start_value) ** (1 / n) - 1 + cagr = (end_value / start_value) ** (1 / n) - 1 else: - CAGR = 0 - return CAGR + cagr = 0 + return cagr # build the matrix to reflect #of days in each month @@ -476,13 +484,13 @@ def month_shift(month): # return new_dict #return (2016,2,29 : 99.86%; present_date: 100%) # form inflated items, based on a 12 month list, present time is the last month of 12 months list -def form_inflated_item(base_list,target_terms,inflation_coeff_dict,present_date): - pre_year_date=datetime.date(present_date.year-1,present_date.month+1,cal_last_day(present_date.year-1,present_date.month+1)) - base_terms=form_date_calendar(pre_year_date,present_date)[1] - for term in base_terms: - base_list[term.month-1]=base_list[term.month-1]/inflation_coeff_dict[term] - new_dict=dict(zip([],[])) - for term in target_terms: - new_dict[term]=base_list[term.month-1]*inflation_coeff_dict[term] - return [new_dict[term] for term in sorted(new_dict)] +# def form_inflated_item(base_list,target_terms,inflation_coeff_dict,present_date): +# pre_year_date=datetime.date(present_date.year-1,present_date.month+1,cal_last_day(present_date.year-1,present_date.month+1)) +# base_terms=form_date_calendar(pre_year_date,present_date)[1] +# for term in base_terms: +# base_list[term.month-1]=base_list[term.month-1]/inflation_coeff_dict[term] +# new_dict=dict(zip([],[])) +# for term in target_terms: +# new_dict[term]=base_list[term.month-1]*inflation_coeff_dict[term] +# return [new_dict[term] for term in sorted(new_dict)] diff --git a/bpfin/tests/test_financials/test_financial_lib.py b/bpfin/tests/test_financials/test_financial_lib.py index 6bf20bdf83cd71fbb92ee86b5b43c1b694df19a3..b756a240da12420cb5347957009d29eb7e515ba6 100644 --- a/bpfin/tests/test_financials/test_financial_lib.py +++ b/bpfin/tests/test_financials/test_financial_lib.py @@ -1,11 +1,23 @@ import datetime from bpfin.financials import financial_lib as fl from bpfin.tests.testdata import sample_data as db +from bpfin.financials.income_statement_form_hist import form_income_statement_hist + + +def test_organize_bill_overview(): + input_bill_overview = db.bill_overview + input_analysis_date = db.analysis_date + output_dict = db.bill_overview_organized + result_dict = fl.organize_bill_overview(input_bill_overview, input_analysis_date) + assert output_dict == result_dict def test_income_statement_single_year(): - income_input = db.income_input_2016 - bill_overview = db.bill_overview + input_income = db.income_input_2016 + input_bill_overview_organized = db.bill_overview_organized output_dict = db.income_statement_2016 - result_dict = fl.income_statement_single_year(income_input, bill_overview) + result_dict = fl.income_statement_single_year(2016, input_income, input_bill_overview_organized) assert output_dict == result_dict + + + diff --git a/bpfin/tests/test_financials/test_income_statement_form_hist.py b/bpfin/tests/test_financials/test_income_statement_form_hist.py new file mode 100644 index 0000000000000000000000000000000000000000..9114f18d001440f59571e01f043750bb624973ad --- /dev/null +++ b/bpfin/tests/test_financials/test_income_statement_form_hist.py @@ -0,0 +1,28 @@ +from bpfin.tests.testdata import sample_data as db +from bpfin.financials.income_statement_form_hist import income_statement_character +from bpfin.financials.income_statement_form_hist import form_income_statement_hist + + +def test_income_statement_character(): + input_income_statement_hist = db.income_statement_full + output_dict = { + 'cagr': 0.05409255338945984, + 'other_utility_percent': 0.21880340182104946, + 'non_utility_expense_percent': 0.03333333333333333, + 'revenue_average': 95000.0, + 'start_year': 2014, + 'end_year': 2016} + result_dict = income_statement_character(input_income_statement_hist) + assert output_dict == result_dict + + +def test_form_income_statement_hist(): + input_raw_income_input = db.raw_income_input + input_bill_overview_organized = db.bill_overview_organized + input_analysis_date = db.analysis_date + output_dict = db.income_statement_full + result_dict = form_income_statement_hist( + input_raw_income_input, + input_bill_overview_organized, + input_analysis_date) + assert output_dict == result_dict diff --git a/bpfin/tests/test_financials/test_income_statement_proj_input.py b/bpfin/tests/test_financials/test_income_statement_proj_input.py new file mode 100644 index 0000000000000000000000000000000000000000..d62d326eb734010da106f9442e1ff8470ff72deb --- /dev/null +++ b/bpfin/tests/test_financials/test_income_statement_proj_input.py @@ -0,0 +1,17 @@ +from bpfin.financials import financial_lib as fl +from bpfin.tests.testdata import sample_data as db +from bpfin.financials.income_statement_next import income_statement_next + + +def test_income_statement_next(): + input_income_statement_hist = db.income_statement_full + input_bill_overview_organized = db.bill_overview_organized + growth_flag_tuple = {1: 0.01, 2: 0.00, 3: -1.0, 4: -2.0} + for i in range(4): + input_growth_rate_flag = growth_flag_tuple[i + 1] + output_dict = db.income_next[i + 1] + result_dict = income_statement_next( + input_income_statement_hist, + input_bill_overview_organized, + input_growth_rate_flag) + assert output_dict == result_dict diff --git a/bpfin/tests/test_utilbills/test_bill_lib.py b/bpfin/tests/test_utilbills/test_bill_lib.py index f1a011f6a8158863722cebec67fcf372dbf00467..c6f14101397cca86472676bb404d564a97d339eb 100644 --- a/bpfin/tests/test_utilbills/test_bill_lib.py +++ b/bpfin/tests/test_utilbills/test_bill_lib.py @@ -1,6 +1,6 @@ from bpfin.utilbills.bill_lib import add_list, annualizing_projection -from datetime import date from bpfin.utilbills import bill_lib as bl +from datetime import date, datetime def test_add_list(): @@ -24,6 +24,14 @@ def test_annualizing_projection(): assert output_dictionary == result +def test_form_bill_year(): + start_date = datetime(2012, 2, 14) + year_term = 5 + output_list = [2012, 2013, 2014, 2015, 2016] + result_list = bl.form_bill_year(start_date, year_term) + assert output_list == result_list + + def test_cal_oil_price(): input_oiluse = [1, 2, 3, 4, 5, 6] input_oilcharge = [12, 10, 8, 6, 4, 2] diff --git a/bpfin/tests/testdata/sample_data.py b/bpfin/tests/testdata/sample_data.py index 5af28528fc69065f95ce477607f8f0b9024499fc..bd9127bf7d2920f499a561d4fa830cb31d306cfa 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -40,7 +40,6 @@ raw_bill['usage'] = [ 0.092170964664193167, 0.094173672324921492, 0.095187070309577237, 0.097177529967719486, 0.095676831493476874, 0.091583582602530869, 0.089122740090923164, 0.091516956021057888] - monthly_normalized_bill = { 'monthly_usage': [ 0.092656544646156233, 0.081407077375199768, 0.096259845999318444, 0.095904698506193486, @@ -75,16 +74,168 @@ monthly_normalized_bill_oil = { 28508.83873646447, 28508.83873646447, 28508.83873646447, 28508.83873646447, 28508.83873646447, 28508.83873646447, 28508.83873646447, 28508.83873646447]} -# pro-forma projection +# pro-forma projection time base analysis_date = { 'proforma_start': datetime.date(2012, 1, 15), 'proforma_duration': 25} +# prior_projection results are at lower place in this file, due to its super long length + +# organize annual bill overview from UI +annual_bill_electricity = { + 2012: 32220.590596217822, 2013: 32782.518348206999, 2014: 33293.100530249467, 2015: 33546.761580451312, + 2016: 33844.501703633454, 2017: 34523.27291517199, 2018: 35384.966283060414, 2019: 36251.490761516034, + 2020: 37100.197369238413, 2021: 37887.082013632862, 2022: 38603.765037606507, 2023: 39307.925216552088, + 2024: 40028.077679374117, 2025: 40772.42926238777, 2026: 41575.666985412594, 2027: 42439.918590730398, + 2028: 43325.235463581725, 2029: 44211.298795796341, 2030: 45097.667796776521, 2031: 45986.665329650692, + 2032: 46886.305428683903, 2033: 47806.578908870637, 2034: 48751.551094031129, 2035: 49722.255450564422, + 2036: 50718.832984188753} +annual_bill_gas = {2015: 1020, 2016: 1220, 2017: 1520} +annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} +annual_bill_water = {2015: 0, 2016: 0, 2017: 0} + +bill_overview = { + 'electricity': [annual_bill_electricity, True], + 'gas': [annual_bill_gas, False], + 'oil': [annual_bill_oil, False], + 'water': [annual_bill_water, False] +} + +bill_overview_organized = { + 'electricity': { + 2012: 32220.590596217822, 2013: 32782.518348207, 2014: 33293.10053024947, + 2015: 33546.76158045131, 2016: 33844.501703633454, 2017: 34523.27291517199, + 2018: 35384.966283060414, 2019: 36251.490761516034, 2020: 37100.19736923841, + 2021: 37887.08201363286, 2022: 38603.76503760651, 2023: 39307.92521655209, + 2024: 40028.07767937412, 2025: 40772.42926238777, 2026: 41575.666985412594, + 2027: 42439.9185907304, 2028: 43325.235463581725, 2029: 44211.29879579634, + 2030: 45097.66779677652, 2031: 45986.66532965069, 2032: 46886.3054286839, + 2033: 47806.57890887064, 2034: 48751.55109403113, 2035: 49722.25545056442, + 2036: 50718.83298418875}, + 'gas': {2015: 1020, 2016: 1220, 2017: 1520, 2012: 1253.3333333333333, + 2013: 1253.3333333333333, 2014: 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}, + '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}, + 'water': {2015: 0, 2016: 0, 2017: 0, 2012: 0.0, 2013: 0.0, 2014: 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} +} + +# income statement test +income_input_2016 = { + 'revenue': 100000, + 'utility_expense': 60000, + 'non_utility_expense': 3000} + +income_statement_2016 = { + 'year': 2016, + 'revenue': 100000, + 'utility_expense': 60000, + 'energy_opex': 36274.501703633454, + 'electricity_opex': 33844.501703633454, + 'gas_opex': 1220, + 'oil_opex': 1210, + 'water_opex': 0, + 'other_utility': 23725.498296366546, + 'non_utility_expense': 3000, + 'net_non_energy_opex': 26725.498296366546, + 'total_opex': 63000.0, + 'noi': 37000.0} + +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 + } +} + +income_statement_full = { + 2014: { + 'year': 2014, 'revenue': 90000, 'utility_expense': 55000, 'energy_opex': 35789.76719691614, + 'electricity_opex': 33293.10053024947, 'gas_opex': 1253.3333333333333, + 'oil_opex': 1243.3333333333333, 'water_opex': 0.0, + 'other_utility': 19210.23280308386, 'non_utility_expense': 3500, 'net_non_energy_opex': 22710.23280308386, + 'total_opex': 58500.0, 'noi': 31500.0}, + 2015: { + 'year': 2015, 'revenue': 95000, 'utility_expense': 55000, 'energy_opex': 35576.76158045131, + 'electricity_opex': 33546.76158045131, 'gas_opex': 1020, 'oil_opex': 1010, 'water_opex': 0, + 'other_utility': 19423.23841954869, 'non_utility_expense': 3000, 'net_non_energy_opex': 22423.23841954869, + 'total_opex': 58000.0, 'noi': 37000.0}, + 2016: { + 'year': 2016, 'revenue': 100000, 'utility_expense': 60000, 'energy_opex': 36274.501703633454, + 'electricity_opex': 33844.501703633454, 'gas_opex': 1220, 'oil_opex': 1210, 'water_opex': 0, + 'other_utility': 23725.498296366546, 'non_utility_expense': 3000, 'net_non_energy_opex': 26725.498296366546, + 'total_opex': 63000.0, 'noi': 37000.0} +} + +# income_input_next = { +# 'year': 2017, 'revenue': 105409.25533894598, 'utility_expense': 60617.176566756985, 'energy_opex': 37553.27291517199, +# 'electricity_opex': 34523.27291517199, 'gas_opex': 1520, 'oil_opex': 1510, 'water_opex': 0, +# 'other_utility': 23063.903651585, 'non_utility_expense': 3513.6418446315324, 'net_non_energy_opex': 26577.54549621653, +# 'total_opex': 64130.81841138852, 'noi': 41278.43692755746} + +income_next = {} + +# growth_rate_flag == 0.01 +income_next[1] = { + 'year': 2017, 'revenue': 101000.0, 'utility_expense': 59652.416499097984, 'energy_opex': 37553.27291517199, + 'electricity_opex': 34523.27291517199, 'gas_opex': 1520, 'oil_opex': 1510, 'water_opex': 0, + 'other_utility': 22099.143583925994, 'non_utility_expense': 3366.6666666666665, 'net_non_energy_opex': 25465.81025059266, + 'total_opex': 63019.08316576465, 'noi': 37980.91683423535} +# growth_rate_flag == 0.00 +income_next[2] = { + 'year': 2017, 'revenue': 100000.0, 'utility_expense': 59433.61309727693, 'energy_opex': 37553.27291517199, + 'electricity_opex': 34523.27291517199, 'gas_opex': 1520, 'oil_opex': 1510, 'water_opex': 0, + 'other_utility': 21880.340182104945, 'non_utility_expense': 3333.3333333333335, 'net_non_energy_opex': 25213.673515438277, + 'total_opex': 62766.94643061027, 'noi': 37233.05356938973} +# growth_rate_flag == -1.0 +income_next[3] = { + 'year': 2017, 'revenue': 95000.0, 'utility_expense': 58339.59608817169, 'energy_opex': 37553.27291517199, + 'electricity_opex': 34523.27291517199, 'gas_opex': 1520, 'oil_opex': 1510, 'water_opex': 0, + 'other_utility': 20786.3231729997, 'non_utility_expense': 3166.6666666666665, 'net_non_energy_opex':23952.989839666367, + 'total_opex': 61506.26275483836, 'noi': 33493.73724516164} +# growth_rate_flag == -2.0 +income_next[4] = { + 'year': 2017, 'revenue': 105409.25533894598, 'utility_expense': 60617.176566756985, 'energy_opex': 37553.27291517199, + 'electricity_opex': 34523.27291517199, 'gas_opex': 1520, 'oil_opex': 1510, 'water_opex': 0, + 'other_utility': 23063.903651585, 'non_utility_expense': 3513.6418446315324, 'net_non_energy_opex': 26577.54549621653, + 'total_opex': 64130.81841138852, 'noi': 41278.43692755746} + +# liability, mortgage and other debt data liability_dictionary = { 'debt1': (150, 'NYSERDA', 10, datetime.date(2012, 12, 31)), 'debt2': (100, 'NYCEEC', 20, datetime.date(2012, 8, 31)) } + +# pro-forma date and bill projection - +# proforma_date_from, proforma_date_to +# prior_rough, post_rough +# piror_regress, post_regress proforma_date_from = [ datetime.date(2012, 1, 1), datetime.date(2012, 2, 1), @@ -862,45 +1013,9 @@ prior_proj_rough_price = [ 42131.00318614885, 40054.83089682108, 40079.848368848034, 40883.798098440246, 40432.470041231565, 40779.34931771414, 41139.21219179594, 40321.43463472525, 43237.085994998866, 48437.35375945405, 51641.29962190506, 48278.23252154697] -annual_bill_electricity ={ - 2012: 32220.590596217822, 2013: 32782.518348206999, 2014: 33293.100530249467, 2015: 33546.761580451312, - 2016: 33844.501703633454, 2017: 34523.27291517199, 2018: 35384.966283060414, 2019: 36251.490761516034, - 2020: 37100.197369238413, 2021: 37887.082013632862, 2022: 38603.765037606507, 2023: 39307.925216552088, - 2024: 40028.077679374117, 2025: 40772.42926238777, 2026: 41575.666985412594, 2027: 42439.918590730398, - 2028: 43325.235463581725, 2029: 44211.298795796341, 2030: 45097.667796776521, 2031: 45986.665329650692, - 2032: 46886.305428683903, 2033: 47806.578908870637, 2034: 48751.551094031129, 2035: 49722.255450564422, - 2036: 50718.832984188753} -annual_bill_gas = {2015: 1020, 2016: 1220, 2017: 1520} -annual_bill_oil = {2015: 1010, 2016: 1210, 2017: 1510} -annual_bill_water = {2015: 0, 2016: 0, 2017: 0} -bill_overview = { - 'electricity': annual_bill_electricity, - 'gas': annual_bill_gas, - 'oil': annual_bill_oil, - 'water': annual_bill_water} - -income_input_2016 = { - 'year': 2016, - 'revenue': 100000, - 'utility_expense': 60000, - 'non_utility_expense': 3000} - -income_statement_2016 = { - 'year': 2016, - 'revenue': 100000, - 'utility_expense': 60000, - 'energy_opex': 36274.501703633454, - 'electricity_opex': 33844.501703633454, - 'gas_opex': 1220, - 'oil_opex': 1210, - 'water_opex': 0, - 'other_utility': 23725.498296366546, - 'non_utility_expense': 3000, - 'net_non_energy_opex': 26725.498296366546, - 'total_opex': 63000.0, - 'noi': 37000.0} - +# global variables - +# inflation coefficient dictionary. this is NOT inflation rate inflation_coeff_dict = { datetime.date(1981, 1, 31): 0.3381208260704529, datetime.date(1981, 2, 28): 0.3410451203881943, diff --git a/bpfin/utilbills/bill_lib.py b/bpfin/utilbills/bill_lib.py index cd810f9ce41844be2225901a9dc940b0b5883fa6..9411a12fabc62b33663fe187013a8b882efb5d2b 100644 --- a/bpfin/utilbills/bill_lib.py +++ b/bpfin/utilbills/bill_lib.py @@ -2,6 +2,7 @@ import statsmodels.api as sm import datetime import calendar import pandas as pd +import copy def add_list(obj_list, number): @@ -51,6 +52,27 @@ def form_bill_calendar(date_start, year_term): return [bdstart, bdend] +def form_bill_year(date_start, year_term): + """Return single list value of years, for length of year_term + + Args: + date_start (datetime): date xx/yy/abcd + year_term (int): number of years + Returns: + list: years + + Description: date_start = 2012,4,5; year_term = 25, output = [2012,...,2036] + """ + bill_year = [date_start.year] + i = 1 + while i < year_term: + bill_year.append(bill_year[-1] + 1) + i = i + 1 + if i == 9999: + break + return bill_year + + def form_date_calendar(date_start, date_end): """Return list of all calendar dates. @@ -180,11 +202,12 @@ def form_inflated_item(base_list, target_terms, inflation_coeff_dict, present_da present_date.month + 1, cal_last_day(present_date.year - 1, present_date.month + 1)) base_terms = form_date_calendar(pre_year_date, present_date)[1] + base_list_copy = copy.deepcopy(base_list) for term in base_terms: - base_list[term.month - 1] = base_list[term.month-1] / inflation_coeff_dict[term] + base_list_copy[term.month - 1] = base_list_copy[term.month-1] / inflation_coeff_dict[term] new_dict = dict(zip([], [])) for term in target_terms: - new_dict[term] = base_list[term.month - 1] * inflation_coeff_dict[term] + new_dict[term] = base_list_copy[term.month - 1] * inflation_coeff_dict[term] return [new_dict[term] for term in sorted(new_dict)]