diff --git a/bpfin/financials/income_statement_form_hist.py b/bpfin/financials/income_statement_form_hist.py index 401d66b52dabef3aeeb4c88897702716f85387f4..706b8ba82024567e9b79e7dbf04e72c42be8a76a 100644 --- a/bpfin/financials/income_statement_form_hist.py +++ b/bpfin/financials/income_statement_form_hist.py @@ -12,7 +12,8 @@ def income_statement_character(income_statement_hist): 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%, ..} - """ + Note: cagr == compound annual growht rate + ''' sorted_income_hist_year = sorted(income_statement_hist) start_year = sorted_income_hist_year[0] end_year = sorted_income_hist_year[-1] @@ -24,7 +25,7 @@ def income_statement_character(income_statement_hist): revenue_sum = 0 other_utility_sum = 0 non_utility_expense_sum = 0 - revenue_average = 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'] @@ -35,7 +36,7 @@ def income_statement_character(income_statement_hist): '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) + 'revenue_average': revenue_sum / (end_year - start_year + 1) # average revenue } return result_dict diff --git a/bpfin/financials/income_statement_projection.py b/bpfin/financials/income_statement_projection.py new file mode 100644 index 0000000000000000000000000000000000000000..ac4d5890a4eaf37b81a079ae88ecf17a41106b52 --- /dev/null +++ b/bpfin/financials/income_statement_projection.py @@ -0,0 +1,46 @@ +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 +import pprint + + +def income_statement_projection(income_statement_hist, bill_overview_organized, growth_rate_flag, analysis_date): + """ + Project income statement tp future multiple years. inputs are historical income statements and annual bills. + 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 with average + 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 + Returns: + dictionary: multiple year full income statement proforma + Note: cagr == compound annual growht rate + """ + characters = income_statement_character(income_statement_hist) + proforma_year = bl.form_bill_year(analysis_date['proforma_start'], + analysis_date['proforma_duration']) + result_dict = {} + last_revenue = income_statement_hist[characters['end_year']]['revenue'] + + for year in proforma_year: + if year <= characters['end_year']: + continue + income_next = fl.Income_Statement() + income_next.year = year + + 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 = last_revenue * (1 + growth_rate) + else: + growth_rate = growth_rate_flag # 0.00, 0.01, 0.02 ... + income_next.revenue = last_revenue * (1 + growth_rate) + + income_next.assign_next(characters, bill_overview_organized) + result_dict[year] = fl.convert_income_statement_class(income_next) + last_revenue = income_next.revenue + return result_dict diff --git a/bpfin/tests/test_financials/test_income_statement_projection.py b/bpfin/tests/test_financials/test_income_statement_projection.py new file mode 100644 index 0000000000000000000000000000000000000000..ede9633e4ed671c14f9add675dbfdbb1ea6720a8 --- /dev/null +++ b/bpfin/tests/test_financials/test_income_statement_projection.py @@ -0,0 +1,21 @@ +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 + + +from bpfin.tests.testdata import sample_data as db +from bpfin.financials.income_statement_projection import income_statement_projection + + +def test_income_statement_projection(): + input_income_statement_hist = db.income_statement_full + input_bill_overview_organized = db.bill_overview_organized + input_growth_rate_flag = -2.0 + input_analysis_date = db.analysis_date + output_dict = db.income_statement_projection_cagr + result_dict = income_statement_projection( + input_income_statement_hist, + input_bill_overview_organized, + input_growth_rate_flag, + input_analysis_date) + assert output_dict == result_dict diff --git a/bpfin/tests/testdata/sample_data.py b/bpfin/tests/testdata/sample_data.py index bd9127bf7d2920f499a561d4fa830cb31d306cfa..38070137dc53a3d0fbb1b8c2ba2b4020dc11f803 100644 --- a/bpfin/tests/testdata/sample_data.py +++ b/bpfin/tests/testdata/sample_data.py @@ -225,6 +225,269 @@ income_next[4] = { 'other_utility': 23063.903651585, 'non_utility_expense': 3513.6418446315324, 'net_non_energy_opex': 26577.54549621653, 'total_opex': 64130.81841138852, 'noi': 41278.43692755746} +income_statement_projection_cagr = { + 2017: {'electricity_opex': 34523.27291517199, + 'energy_opex': 37553.27291517199, + 'gas_opex': 1520, + 'net_non_energy_opex': 26577.54549621653, + 'noi': 41278.43692755746, + 'non_utility_expense': 3513.6418446315324, + 'oil_opex': 1510, + 'other_utility': 23063.903651585, + 'revenue': 105409.25533894598, + 'total_opex': 64130.81841138852, + 'utility_expense': 60617.176566756985, + 'water_opex': 0, + 'year': 2017}, + 2018: {'electricity_opex': 35384.966283060414, + 'energy_opex': 37881.632949727085, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 28015.192794931423, + 'noi': 45214.28536645262, + 'non_utility_expense': 3703.703703703704, + 'oil_opex': 1243.3333333333333, + 'other_utility': 24311.48909122772, + 'revenue': 111111.11111111112, + 'total_opex': 65896.8257446585, + 'utility_expense': 62193.122040954804, + 'water_opex': 0.0, + 'year': 2018}, + 2019: {'electricity_opex': 36251.490761516034, + 'energy_opex': 38748.157428182705, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 29530.60610690726, + 'noi': 48842.631285961135, + 'non_utility_expense': 3904.0464940350366, + 'oil_opex': 1243.3333333333333, + 'other_utility': 25626.559612872225, + 'revenue': 117121.3948210511, + 'total_opex': 68278.76353508997, + 'utility_expense': 64374.71704105493, + 'water_opex': 0.0, + 'year': 2019}, + 2020: {'electricity_opex': 37100.19736923841, + 'energy_opex': 39596.864035905084, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 31127.99199436825, + 'noi': 52731.934093183474, + 'non_utility_expense': 4115.22633744856, + 'oil_opex': 1243.3333333333333, + 'other_utility': 27012.76565691969, + 'revenue': 123456.79012345681, + 'total_opex': 70724.85603027334, + 'utility_expense': 66609.62969282478, + 'water_opex': 0.0, + 'year': 2020}, + 2021: {'electricity_opex': 37887.08201363286, + 'energy_opex': 40383.74868029953, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 32811.78456323029, + 'noi': 56939.349890971425, + 'non_utility_expense': 4337.829437816708, + 'oil_opex': 1243.3333333333333, + 'other_utility': 28473.955125413588, + 'revenue': 130134.88313450124, + 'total_opex': 73195.53324352982, + 'utility_expense': 68857.70380571313, + 'water_opex': 0.0, + 'year': 2021}, + 2022: {'electricity_opex': 38603.76503760651, + 'energy_opex': 41100.43170427318, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 34586.65777152029, + 'noi': 61487.1217724919, + 'non_utility_expense': 4572.4737082761785, + 'oil_opex': 1243.3333333333333, + 'other_utility': 30014.184063244105, + 'revenue': 137174.21124828537, + 'total_opex': 75687.08947579347, + 'utility_expense': 71114.61576751729, + 'water_opex': 0.0, + 'year': 2022}, + 2023: {'electricity_opex': 39307.92521655209, + 'energy_opex': 41804.59188321876, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 36457.53840358923, + 'noi': 66332.18430708232, + 'non_utility_expense': 4819.81048646301, + 'oil_opex': 1243.3333333333333, + 'other_utility': 31637.727917126216, + 'revenue': 144594.3145938903, + 'total_opex': 78262.13028680798, + 'utility_expense': 73442.31980034498, + 'water_opex': 0.0, + 'year': 2023}, + 2024: {'electricity_opex': 40028.07767937412, + 'energy_opex': 42524.74434604079, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 38429.619746133656, + 'noi': 71461.4261836982, + 'non_utility_expense': 5080.526342529089, + 'oil_opex': 1243.3333333333333, + 'other_utility': 33349.093403604566, + 'revenue': 152415.79027587266, + 'total_opex': 80954.36409217445, + 'utility_expense': 75873.83774964535, + 'water_opex': 0.0, + 'year': 2024}, + 2025: {'electricity_opex': 40772.42926238777, + 'energy_opex': 43269.09592905444, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 40508.37600398803, + 'noi': 76882.87761572453, + 'non_utility_expense': 5355.3449849589, + 'oil_opex': 1243.3333333333333, + 'other_utility': 35153.03101902913, + 'revenue': 160660.349548767, + 'total_opex': 83777.47193304247, + 'utility_expense': 78422.12694808358, + 'water_opex': 0.0, + 'year': 2025}, + 2026: {'electricity_opex': 41575.666985412594, + 'energy_opex': 44072.333652079265, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 42699.57749570406, + 'noi': 82578.96693651963, + 'non_utility_expense': 5645.029269476766, + 'oil_opex': 1243.3333333333333, + 'other_utility': 37054.5482262273, + 'revenue': 169350.87808430297, + 'total_opex': 86771.91114778334, + 'utility_expense': 81126.88187830657, + 'water_opex': 0.0, + 'year': 2026}, + 2027: {'electricity_opex': 42439.9185907304, + 'energy_opex': 44936.58525739707, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 45009.30667109782, + 'noi': 88565.60757013515, + 'non_utility_expense': 5950.383316621001, + 'oil_opex': 1243.3333333333333, + 'other_utility': 39058.923354476814, + 'revenue': 178511.49949863003, + 'total_opex': 89945.89192849488, + 'utility_expense': 83995.50861187388, + 'water_opex': 0.0, + 'year': 2027}, + 2028: {'electricity_opex': 43325.235463581725, + 'energy_opex': 45821.902130248396, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 47443.974995226745, + 'noi': 94901.76519041706, + 'non_utility_expense': 6272.254743863074, + 'oil_opex': 1243.3333333333333, + 'other_utility': 41171.72025136367, + 'revenue': 188167.6423158922, + 'total_opex': 93265.87712547515, + 'utility_expense': 86993.62238161208, + 'water_opex': 0.0, + 'year': 2028}, + 2029: {'electricity_opex': 44211.29879579634, + 'energy_opex': 46707.96546246301, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 50010.34074566425, + 'noi': 101627.80434590613, + 'non_utility_expense': 6611.5370184677795, + 'oil_opex': 1243.3333333333333, + 'other_utility': 43398.80372719647, + 'revenue': 198346.1105540334, + 'total_opex': 96718.30620812727, + 'utility_expense': 90106.76918965948, + 'water_opex': 0.0, + 'year': 2029}, + 2030: {'electricity_opex': 45097.66779677652, + 'energy_opex': 47594.33446344319, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 52715.52777247417, + 'noi': 108765.29589285178, + 'non_utility_expense': 6969.171937625638, + 'oil_opex': 1243.3333333333333, + 'other_utility': 45746.35583484853, + 'revenue': 209075.15812876914, + 'total_opex': 100309.86223591736, + 'utility_expense': 93340.69029829172, + 'water_opex': 0.0, + 'year': 2030}, + 2031: {'electricity_opex': 45986.66532965069, + 'energy_opex': 48483.33199631736, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 55567.04527296028, + 'noi': 116334.19001298171, + 'non_utility_expense': 7346.152242741978, + 'oil_opex': 1243.3333333333333, + 'other_utility': 48220.8930302183, + 'revenue': 220384.56728225935, + 'total_opex': 104050.37726927764, + 'utility_expense': 96704.22502653566, + 'water_opex': 0.0, + 'year': 2031}, + 2032: {'electricity_opex': 46886.3054286839, + 'energy_opex': 49382.972095350575, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 58572.808636082416, + 'noi': 124349.95052275498, + 'non_utility_expense': 7743.524375139598, + 'oil_opex': 1243.3333333333333, + 'other_utility': 50829.28426094282, + 'revenue': 232305.73125418797, + 'total_opex': 107955.78073143298, + 'utility_expense': 100212.2563562934, + 'water_opex': 0.0, + 'year': 2032}, + 2033: {'electricity_opex': 47806.57890887064, + 'energy_opex': 50303.24557553731, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 61741.161414400325, + 'noi': 132827.33443479502, + 'non_utility_expense': 8162.3913808244215, + 'oil_opex': 1243.3333333333333, + 'other_utility': 53578.7700335759, + 'revenue': 244871.74142473264, + 'total_opex': 112044.40698993763, + 'utility_expense': 103882.01560911321, + 'water_opex': 0.0, + 'year': 2033}, + 2034: {'electricity_opex': 48751.55109403113, + 'energy_opex': 51248.2177606978, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 65080.898484536025, + 'noi': 141788.36292608615, + 'non_utility_expense': 8603.915972377332, + 'oil_opex': 1243.3333333333333, + 'other_utility': 56476.98251215869, + 'revenue': 258117.47917131998, + 'total_opex': 116329.11624523383, + 'utility_expense': 107725.20027285648, + 'water_opex': 0.0, + 'year': 2034}, + 2035: {'electricity_opex': 49722.25545056442, + 'energy_opex': 52218.92211723109, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 68601.2904604448, + 'noi': 151259.5001164715, + 'non_utility_expense': 9069.323756471578, + 'oil_opex': 1243.3333333333333, + 'other_utility': 59531.966703973216, + 'revenue': 272079.71269414737, + 'total_opex': 120820.21257767589, + 'utility_expense': 111750.88882120431, + 'water_opex': 0.0, + 'year': 2035}, + 2036: {'electricity_opex': 50718.83298418875, + 'energy_opex': 53215.499650855425, + 'gas_opex': 1253.3333333333333, + 'net_non_energy_opex': 72312.10942726226, + 'noi': 161269.59000112678, + 'non_utility_expense': 9559.906635974816, + 'oil_opex': 1243.3333333333333, + 'other_utility': 62752.20279128744, + 'revenue': 286797.19907924446, + 'total_opex': 125527.60907811768, + 'utility_expense': 115967.70244214287, + 'water_opex': 0.0, + 'year': 2036}} + + # liability, mortgage and other debt data liability_dictionary = { 'debt1': (150, 'NYSERDA', 10, datetime.date(2012, 12, 31)),