diff --git a/bpfin/financials/cash_balance.py b/bpfin/financials/cash_balance.py index 6b43215d6a6786b02a253a1b0f1bdeec3f89dbae..1076083e300d40a76adfe14997125056032ce947 100644 --- a/bpfin/financials/cash_balance.py +++ b/bpfin/financials/cash_balance.py @@ -38,8 +38,7 @@ def cash_balance(analysis_date, cash_dictionary): if year in cash_balance_dictionary: final_dict[year] = cash_balance_dictionary[year] if year < min(cash_balance_dictionary): - min_value = min(cash_balance_dictionary) - final_dict[year] = cash_balance_dictionary[min_value] + final_dict[year] = cash_average if year > min(cash_balance_dictionary) and year < max(cash_balance_dictionary): if year not in cash_balance_dictionary: final_dict[year] = cash_average diff --git a/bpfin/financials/financial_balance.py b/bpfin/financials/financial_balance.py new file mode 100644 index 0000000000000000000000000000000000000000..b8643c91badb40bb58b49b52819dc14051614cc8 --- /dev/null +++ b/bpfin/financials/financial_balance.py @@ -0,0 +1,180 @@ +import copy +from bpfin.lib import other as lib +from bpfin.utilbills.bill_lib import form_bill_year +from numpy import mean + + +def convert_balance_sheet_class(balance_sheet_class): + balance_sheet_dict = { + 'year': balance_sheet_class.year, + 'cash': balance_sheet_class.cash, + 'other_debt_service': balance_sheet_class.other_debt_service, + 'net_income': balance_sheet_class.net_income + } + return balance_sheet_dict + + +class Balance_Sheet(): + """This is to manage the balance sheet. + In the future this class will contain more important and complex functions. + """ + + def __init__(self): + self.year = None + self.cash = None + # self.current_assets = None + # self.fixed_assets = None + self.other_debt_service = None + # self.current_liabilities = None + # self.fixed_liablities = None + # self.bop_equity = None + # self.cap_holdings = None + self.net_income = None + # self.eop_equity = None + + def put_hist_balance_sheet(self, year, cash, other_debt_service, + net_income): + """Put historical data values into balance sheet based on the cash balance years. + Args: + year (int): year + cash (float): cash value + other_debt_service (float): other debt service value + net_income (float): net income value + """ + self.year = year + self.cash = cash + self.other_debt_service = other_debt_service + self.net_income = net_income + + +class Balance_Sheet_Next(): + def __init__(self, year, last_year_cash, other_debt_service, net_income): + """Create next year value of balance sheet + Args: + year (int): + last_year_cash (float): cash + other_debt_service (float): liability + net_income (float): net income + """ + + self.year = year + self.other_debt_service = other_debt_service + self.net_income = net_income + self.cash = last_year_cash - self.other_debt_service + self.net_income + + +class Balance_Sheet_Table(): + def __init__(self, cash_balance_dictionary, other_debt_service_dictionary, + net_income_dictionary): + """The balance sheet table and the projections (for now) is composed of cash, liability, and net_income. + Together, these three components allow us to project the future balance sheet, which is essentially cash available. + This class includes the historical balance sheet table and also the future projection of the balance sheet table. + Args: + year (integer): year + cash_balance_dictionary (dict): dictionary of known annual cash value items, key is year + other_debt_service_dictionary (dict): dictionary of known annual liability value items, key is year + net_income_dictionary (dict): dictionary of known annual net_income value items, key is year + """ + + self.hist_start_year = None + self.hist_end_year = None + self.hist_balance_sheet_table = [] + # entire table, not just historical + self.bs_table = [] + + for year in sorted(cash_balance_dictionary): + current_balance_sheet = Balance_Sheet() + if year in net_income_dictionary: + net_income = net_income_dictionary[year] + else: + net_income = None + current_balance_sheet.put_hist_balance_sheet( + year, cash_balance_dictionary[year], + other_debt_service_dictionary[year], + net_income) + self.hist_balance_sheet_table.append(current_balance_sheet) + + sorted_balance_sheet_hist_year = sorted(cash_balance_dictionary) + self.hist_start_year = sorted_balance_sheet_hist_year[0] + self.hist_end_year = sorted_balance_sheet_hist_year[-1] + + self.bs_table = copy.deepcopy(self.hist_balance_sheet_table) + + def project_balance_sheet(self, analysis_date, + other_debt_service_dictionary, + net_income_dictionary): + """This function projects the balance sheet based on the given historical balance sheet information. + It uses projections of other_debt_service (liability) and net_income to forecast how cash will change over time. + The analysis_date lets us know how far in the future we need to project. + Args: + analysis_date (dict): {pro_forma date: duration (years)} + other_debt_service_dictionary (dict): dictionary of debt service values per year + net_income_dictionary (dict): dictionary of net income values per years + Returns: + dictionary: full dictionary of dictionaries of balance sheet entries + """ + + proforma_year = form_bill_year(analysis_date['proforma_start'], + analysis_date['proforma_duration']) + current_table = copy.deepcopy(self.hist_balance_sheet_table) + for year in proforma_year: + last_year_cash = current_table[-1].cash + if year <= current_table[-1].year: + continue + current_other_debt_service = 0.00 + current_net_income = 0.00 + if year in other_debt_service_dictionary: + current_other_debt_service = other_debt_service_dictionary[ + year] + if year in net_income_dictionary: + current_net_income = net_income_dictionary[year] + current_balance_sheet = Balance_Sheet_Next( + year, last_year_cash, current_other_debt_service, + current_net_income) + current_table.append(current_balance_sheet) + self.bs_table = current_table + # return current_table + + def get_hist_balance_sheet_table(self): + hist_balance_sheet_dict = {} + for current_balance_sheet in self.hist_balance_sheet_table: + hist_balance_sheet_dict[current_balance_sheet.year] = { + 'other_debt_service': current_balance_sheet.other_debt_service, + 'net_income': current_balance_sheet.net_income, + 'cash': current_balance_sheet.cash + } + return hist_balance_sheet_dict + + def get_single_year_balance_sheet(self, year): + + for current_balance_sheet in self.bs_table: + if current_balance_sheet.year == year: + return { + current_balance_sheet.year: { + 'other_debt_service': + current_balance_sheet.other_debt_service, + 'net_income': + current_balance_sheet.net_income, + 'cash': + current_balance_sheet.cash + } + } + return + + def get_first_year_cash(self, commission_date): + + first_year = commission_date.year + 1 + for current_balance_sheet in self.bs_table: + if current_balance_sheet.year == first_year: + return current_balance_sheet.cash + + def get_cash_dict(self): + """ + Get a dictionary of cash. + Return: + dictionary: cash for each year in balance sheet table. Key is year + """ + cash_dict = {} + for current_balance_sheet in self.bs_table: + cash_dict[current_balance_sheet.year] = current_balance_sheet.cash + return cash_dict diff --git a/bpfin/financials/financial_lib.py b/bpfin/financials/financial_lib.py index 253c1ec675a6ad115ba6f9901360d9adbe8d9add..726fa09674ca13a23397e0534425c9a51174fdac 100644 --- a/bpfin/financials/financial_lib.py +++ b/bpfin/financials/financial_lib.py @@ -533,150 +533,6 @@ class Income_Statement_Table(): return total_energy_dict -class Balance_Sheet(): - def __init__(self): - self.year = None - self.cash = None - # self.current_assets = None - # self.fixed_assets = None - self.other_debt_service = None - # self.current_liabilities = None - # self.fixed_liablities = None - # self.bop_equity = None - # self.cap_holdings = None - self.net_income = None - # self.eop_equity = None - - def put_hist_balance_sheet(self, year, balance_sheet_input): - """Put historical data values into balance sheet based on the cash balance years. - Args: - year (int): year - balance_sheet_input (dictionary): dictionary of balance sheet items: cash, noi, debt_service - Returns: - dictionary: filled in balance sheet - """ - self.year = year - self.cash = balance_sheet_input['cash'][year] - self.other_debt_service = balance_sheet_input['other_debt_service'][ - year] - self.net_income = balance_sheet_input['net_income'][year] - - -def convert_balance_sheet_class(balance_sheet_class): - balance_sheet_dict = { - 'year': balance_sheet_class.year, - 'cash': balance_sheet_class.cash, - 'other_debt_service': balance_sheet_class.other_debt_service, - 'net_income': balance_sheet_class.net_income - } - return balance_sheet_dict - - -class Balance_Sheet_Next(): - def __init__(self, year, last_year_cash, other_debt_service, net_income): - - self.year = year - self.other_debt_service = other_debt_service - self.net_income = net_income - self.cash = last_year_cash - self.other_debt_service + self.net_income - - -class Balance_Sheet_Table(): - def __init__(self, balance_sheet_input): - - self.hist_start_year = None - self.hist_end_year = None - self.hist_balance_sheet_table = [] - self.bs_table = [] - # entire table, not just historical - - for year in balance_sheet_input['cash']: - - current_balance_sheet = Balance_Sheet() - current_balance_sheet.put_hist_balance_sheet( - year, balance_sheet_input) - self.hist_balance_sheet_table.append(current_balance_sheet) - - sorted_balance_sheet_hist_year = sorted(balance_sheet_input['cash']) - self.hist_start_year = sorted_balance_sheet_hist_year[0] - self.hist_end_year = sorted_balance_sheet_hist_year[-1] - - self.bs_table = self.hist_balance_sheet_table - - def project_balance_sheet(self, analysis_date, other_debt_service, - net_income_dictionary): - """Create balance sheet projection. - Args: - analysis_date (dict): {pro_forma date: duration (years)} - other_debt_service (dict): dictionary of debt service values per year - net_income_dictionary (dict): dictionary of net income values per years - Returns: - dictionary: full dictionary of dictionaries of balance sheet entries - """ - - proforma_year = form_bill_year(analysis_date['proforma_start'], - analysis_date['proforma_duration']) - current_table = copy.deepcopy(self.hist_balance_sheet_table) - for year in proforma_year: - last_year_cash = current_table[-1].cash - if year <= current_table[-1].year: - continue - current_other_debt_service = 0.00 - current_net_income = 0.00 - if year in other_debt_service: - current_other_debt_service = other_debt_service[year] - if year in net_income_dictionary: - current_net_income = net_income_dictionary[year] - current_balance_sheet = Balance_Sheet_Next( - year, last_year_cash, current_other_debt_service, - current_net_income) - current_table.append(current_balance_sheet) - self.bs_table = current_table - # return current_table - - def get_hist_balance_sheet_table(self): - hist_balance_sheet_dict = {} - for current_balance_sheet in self.hist_balance_sheet_table: - hist_balance_sheet_dict[current_balance_sheet.year] = { - 'other_debt_service': current_balance_sheet.other_debt_service, - 'net_income': current_balance_sheet.net_income, - 'cash': current_balance_sheet.cash - } - return hist_balance_sheet_dict - - def get_single_year_balance_sheet(self, year): - - for current_balance_sheet in self.bs_table: - if current_balance_sheet.year == year: - return { - current_balance_sheet.year: { - 'other_debt_service': - current_balance_sheet.other_debt_service, - 'net_income': - current_balance_sheet.net_income, - 'cash': - current_balance_sheet.cash - } - } - return None - - def get_first_year_cash(self, commission_date): - - first_year = commission_date.year+1 - for current_balance_sheet in self.bs_table: - if current_balance_sheet.year == first_year: - return current_balance_sheet.cash - - def get_cash_dict(self): - """ - Get a dictionary of cash. - Return: - dictionary: cash for each year in balance sheet table. Key is year - """ - cash_dict = {} - for current_balance_sheet in self.bs_table: - cash_dict[current_balance_sheet.year] = current_balance_sheet.cash - return cash_dict # # ************ guide for front end dev *********** # # First, fill in bill overview, save it. diff --git a/bpfin/tests/test_financials/test_cash_balance.py b/bpfin/tests/test_financials/test_cash_balance.py index 28722261065e4fd4f077ac91a8b88ca16bb8e852..7037a665e45b16400d3f0f9ce746639bb9fae243 100644 --- a/bpfin/tests/test_financials/test_cash_balance.py +++ b/bpfin/tests/test_financials/test_cash_balance.py @@ -2,53 +2,52 @@ from bpfin.financials.cash_balance import cash_balance from datetime import date -# def test_cash_balance_year_gap(): -# input_dictionary = { -# date(2014, 11, 1): (500, False), -# date(2014, 12, 1): (400, False), -# date(2014, 3, 13): (600, False), -# date(2016, 11, 11): (500, False), -# date(2016, 12, 31): (400, True) -# } -# input_analysis_date = { -# 'proforma_start': date(2012, 5, 3), -# 'proforma_duration': 12 -# } -# output_cash_balance = { -# 2012: 450, -# 2013: 450, -# 2014: 500, -# 2015: 450, -# 2016: 400 -# } -# result = cash_balance(input_analysis_date, input_dictionary) -# assert result == output_cash_balance +def test_cash_balance_year_gap(): + input_dictionary = { + date(2014, 11, 1): (500, False), + date(2014, 12, 1): (400, False), + date(2014, 3, 13): (600, False), + date(2016, 11, 11): (500, False), + date(2016, 12, 31): (400, True) + } + input_analysis_date = { + 'proforma_start': date(2012, 5, 3), + 'proforma_duration': 12 + } + output_cash_balance = { + 2012: 450, + 2013: 450, + 2014: 500, + 2015: 450, + 2016: 400 + } + result = cash_balance(input_analysis_date, input_dictionary) + assert result == output_cash_balance -# def test_cash_balance(): -# input_dictionary = { -# date(2014, 11, 1): (500, False), -# date(2015, 12, 31): (600, True), -# date(2016, 11, 11): (500, False), -# date(2016, 10, 10): (400, False) -# } -# input_analysis_date = { -# 'proforma_start': date(2006, 5, 3), -# 'proforma_duration': 12 -# } -# output_cash_balance = { -# 2006: 500, -# 2007: 500, -# 2008: 500, -# 2009: 500, -# 2010: 500, -# 2011: 500, -# 2012: 500, -# 2013: 500, -# 2014: 500.0, -# 2015: 600, -# 2016: 450.0 -# } -# result = cash_balance(input_analysis_date, input_dictionary) - -# assert result == output_cash_balance +def test_cash_balance(): + input_dictionary = { + date(2014, 11, 1): (500, False), + date(2015, 12, 31): (600, True), + date(2016, 11, 11): (300, False), + date(2016, 10, 10): (500, False) + } + input_analysis_date = { + 'proforma_start': date(2006, 5, 3), + 'proforma_duration': 12 + } + output_cash_balance = { + 2006: 500, + 2007: 500, + 2008: 500, + 2009: 500, + 2010: 500, + 2011: 500, + 2012: 500, + 2013: 500, + 2014: 500.0, + 2015: 600, + 2016: 400.0 + } + result = cash_balance(input_analysis_date, input_dictionary) + assert result == output_cash_balance diff --git a/bpfin/tests/test_financials/test_financial_balance.py b/bpfin/tests/test_financials/test_financial_balance.py new file mode 100644 index 0000000000000000000000000000000000000000..97e5407c665f4a87ca229b29471212a44e8b2f6b --- /dev/null +++ b/bpfin/tests/test_financials/test_financial_balance.py @@ -0,0 +1,26 @@ +from bpfin.financials.financial_balance import Balance_Sheet_Table +from bpfin.tests.testdata import sample_data as db +from datetime import datetime, date + + +def test_Balance_Sheet_Table(): + input_raw_cash_balance = db.cash_balance + input_raw_other_debt_service = db.liability_dictionary + input_raw_net_income = db.noi_dictionary + + output_hist_balance_sheet_table = db.hist_balance_sheet + output_single_year_bs_2018 = db.balance_sheet_2018 + output_single_year_bs_2025 = db.balance_sheet_2025 + output_single_year_bs_2030 = db.balance_sheet_2030 + output_first_year_cash = db.hist_balance_sheet[2016]['cash'] + + BS_table = Balance_Sheet_Table(input_raw_cash_balance, input_raw_other_debt_service, input_raw_net_income) + + BS_table.project_balance_sheet(db.analysis_date, db.liability_dictionary, + db.noi_dictionary) + + assert BS_table.get_hist_balance_sheet_table() == output_hist_balance_sheet_table + assert BS_table.get_single_year_balance_sheet(2018) == output_single_year_bs_2018 + assert BS_table.get_single_year_balance_sheet(2025) == output_single_year_bs_2025 + assert BS_table.get_single_year_balance_sheet(2030) == output_single_year_bs_2030 + assert BS_table.get_first_year_cash(datetime(2015, 10, 1)) == output_first_year_cash diff --git a/bpfin/tests/test_financials/test_financial_lib.py b/bpfin/tests/test_financials/test_financial_lib.py index e24e1b62e2ac279fa6458bd51ea0fa705de7d3e2..8d5f6ec582af48fdc6c90379e976b57fac5ec27f 100644 --- a/bpfin/tests/test_financials/test_financial_lib.py +++ b/bpfin/tests/test_financials/test_financial_lib.py @@ -2,7 +2,6 @@ import datetime import bpfin.financials.financial_lib as fl from bpfin.tests.testdata import sample_data as db from bpfin.financials.financial_lib import Income_Statement_Table -from bpfin.financials.financial_lib import Balance_Sheet_Table def test_organize_bill_overview(): @@ -38,27 +37,3 @@ def test_Income_Statement_Table(): 2017) == output_single_year # test get_single_year print(IS_table.get_single_year(2018)) assert IS_table.get_first_year_noi(datetime.date(2017, 3, 14)) == output_first_year_noi - - -def test_Balance_Sheet_Table(): - input_raw_balance_sheet = db.raw_balance_sheet - output_hist_balance_sheet_table = db.hist_balance_sheet - output_single_year_bs_2018 = db.balance_sheet_2018 - output_single_year_bs_2025 = db.balance_sheet_2025 - output_single_year_bs_2030 = db.balance_sheet_2030 - output_first_year_cash = db.hist_balance_sheet[2016]['cash'] - - BS_table = Balance_Sheet_Table(input_raw_balance_sheet) - BS_table.project_balance_sheet(db.analysis_date, db.liability_dictionary, - db.noi_dictionary) - - assert BS_table.get_hist_balance_sheet_table( - ) == output_hist_balance_sheet_table - assert BS_table.get_single_year_balance_sheet( - 2018) == output_single_year_bs_2018 - assert BS_table.get_single_year_balance_sheet( - 2025) == output_single_year_bs_2025 - assert BS_table.get_single_year_balance_sheet( - 2030) == output_single_year_bs_2030 - assert BS_table.get_first_year_cash( - datetime.date(2015, 10, 1)) == output_first_year_cash diff --git a/bpfin/tests/testdata/sample_data.py b/bpfin/tests/testdata/sample_data.py index 4a86f35a3d94c4abcfd752bbfd620c2c0dad6540..25ddfe01031a6b755c93ed553c70a75e585e62ee 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -3026,3 +3026,13 @@ raw_income_input_demo = {2014: {'total_revenue': 200000, 'other_utility_expenses 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}} + +liability_dictionary = { + 2012: 650, + 2013: 2550, + 2014: 100, + 2015: 6000, + 2016: 15000, + 2017: 10000, + 2018: 5000 + }