def __init__(self, btax_refdict, iit_refdict, btax_basedict={}, iit_basedict={}, elast_dict=None, investor_data='puf.csv'): # Set default policy parameters for later use self.btax_defaults = Data().btax_defaults # Create the baseline and reform parameter storing forms self.btax_params_base = self.update_btax_params(btax_basedict) self.btax_params_ref = self.update_btax_params(btax_refdict) # Create Investors self.investor_base = Investor(iit_basedict, investor_data) self.investor_ref = Investor(iit_refdict, investor_data) # Create Corporations self.corp_base = Corporation(self.btax_params_base) self.corp_ref = Corporation(self.btax_params_ref) # Create PassThroughs self.passthru_base = PassThrough(self.btax_params_base) self.passthru_ref = PassThrough(self.btax_params_ref) # Save the elasticity dictionary if elast_dict is not None: self.check_elast_dict(elast_dict) self.elast_dict = elast_dict else: self.elast_dict = Data().elast_defaults
def __init__(self, btax_params, corp=True, data=None, response=None): # Create an associated Data object if isinstance(data, Data): self.data = data else: self.data = Data() # Check inputs if isinstance(corp, bool): self.corp = corp else: raise ValueError('corp must be True or False') if response is None or isinstance(response, pd.DataFrame): self.response = response else: raise ValueError('response must be DataFrame or None') if corp: self.adjustments = { 'bonus': 0.60290131, 'sec179': 0.016687178, 'overall': self.data.adjfactor_dep_corp, 'rescalar': self.data.rescale_corp } else: self.adjustments = { 'bonus': 0.453683778, 'sec179': 0.17299506, 'overall': self.data.adjfactor_dep_noncorp, 'rescalar': self.data.rescale_noncorp } if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame')
def calc_tauE(mtrdict, incdict, year): """ Calculate the effective marginal tax rate on equity income in year. """ # Retained earnings rate m = 0.44 # Nominal expected return to equity iyr = year - START_YEAR E = (Data().econ_defaults['r_e_c'][iyr] + Data().econ_defaults['pi'][iyr]) # shares of cg in short-term, long-term, and held until death omega_scg = 0.034 omega_lcg = 0.496 omega_xcg = 1 - omega_scg - omega_lcg # shares of corp equity in taxable, deferred and nontaxable form alpha_ft = 0.572 alpha_td = 0.039 alpha_nt = 0.389 # holding period for equity h_scg = 0.5 h_lcg = 8.0 h_td = 8.0 # Get MTRs mtr_d = mtrdict['e00650'] mtr_scg = mtrdict['p22250'] mtr_lcg = mtrdict['p23250'] mtr_td = mtrdict['e01700'] # Get income measures inc_d = incdict['div'] inc_scg = np.where(incdict['stcg'] >= 0, incdict['stcg'], 0) inc_lcg = np.where(incdict['ltcg'] >= 0, incdict['ltcg'], 0) inc_td = incdict['definc'] posti = (incdict['taxinc'] > 0.) wgt = incdict['wgt'] # MTR on dividend income tau_d = sum(mtr_d * inc_d * posti * wgt) / sum(inc_d * posti * wgt) # accrual effective mtr on stcg tau_scg1 = (sum(mtr_scg * inc_scg * posti * wgt) / sum(inc_scg * posti * wgt)) tau_scg = (1 - (np.log(np.exp(m * E * h_scg) * (1 - tau_scg1) + tau_scg1) / (m * E * h_scg))) # accrual effective mtr on ltcg tau_lcg1 = (sum(mtr_lcg * inc_lcg * posti * wgt) / sum(inc_lcg * posti * wgt)) tau_lcg = (1 - (np.log(np.exp(m * E * h_lcg) * (1 - tau_lcg1) + tau_lcg1) / (m * E * h_lcg))) # mtr on capital gains held until death tau_xcg = 0.0 tau_cg = (omega_scg * tau_scg + omega_lcg * tau_lcg + omega_xcg * tau_xcg) tau_ft = (1 - m) * tau_d + m * tau_cg tau_td1 = (sum(mtr_td * inc_td * posti * wgt) / sum(inc_td * posti * wgt)) tau_td = (1 - (np.log(np.exp(E * h_td) * (1 - tau_td1) + tau_td1) / (E * h_td))) tau_e = alpha_ft * tau_ft + alpha_td * tau_td + alpha_nt * 0.0 return tau_e
def __init__(self, btax_params): # Store policy parameter objects if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') # Create Data object self.data = Data() # Create Asset object and calculate self.asset = Asset(self.btax_params, corp=False, data=self.data) self.asset.calc_all() # Create earnings forecast self.create_earnings()
def __init__(self, btax_params, earnings, data=None, assets=None, debts=None): # Create an associated Data object if isinstance(data, Data): self.data = data else: self.data = Data() if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') if assets is not None: if isinstance(assets, Asset): self.assets = assets else: raise ValueError('assets must be Asset object') else: self.assets = Asset(btax_params) self.assets.calc_all() if debts is not None: if isinstance(debts, Debt): self.debts = debts else: raise ValueError('debts must be Debt object') else: assets_forecast = self.assets.get_forecast() self.debts = Debt(btax_params, assets_forecast) self.debts.calc_all() # Use earnings to create DataFrame for results assert len(earnings) == 14 combined = pd.DataFrame({'year': range(2014,2028), 'ebitda': earnings}) # Add tax depreciation and net interest deductions combined['taxDep'] = self.assets.get_taxdep() combined['nid'] = self.debts.get_nid() self.combined_return = combined
def _calc_investment_response(self, btax_params_base, btax_params_ref): """ Calculates percent change in investment & marginal product of capital, for each asset type, for each year, corporate and noncorporate. firstyear: when the firm behavioral response takes effect """ # Read in the underlying functions for the investment response maindata = copy.deepcopy(Data().taxdep_info_gross('pre2017')) maindata.drop(['L_gds', 'L_ads', 'Method'], axis=1, inplace=True) # Extract relevant response parameters firstyear = self.elasticities['first_year_response'] elast_c = self.elasticities['inv_usercost_c'] elast_nc = self.elasticities['inv_usercost_nc'] selast_c = self.elasticities['inv_eatr_c'] selast_nc = self.elasticities['inv_eatr_nc'] mne_share_c = self.elasticities['mne_share_c'] mne_share_nc = self.elasticities['mne_share_nc'] # No responses for years before first_year_response for year in range(START_YEAR, firstyear): ystr = str(year) maindata['deltaIc' + ystr] = 0. maindata['deltaInc' + ystr] = 0. maindata['MPKc' + ystr] = 0. maindata['MPKnc' + ystr] = 0. # Calculate cost of capital and EATR for every year for baseline btaxmini_base = BtaxMini(btax_params_base) years = range(firstyear, END_YEAR + 1) results_base = btaxmini_base.run_btax_mini(years) # Calculate cost of capital and EATR for every year for reform btaxmini_ref = BtaxMini(btax_params_ref) results_ref = btaxmini_ref.run_btax_mini(years) # Compare results to produce the responses for year in years: ystr = str(year) maindata['deltaIc' + ystr] = ( ((results_ref['u_c' + ystr] / results_base['u_c' + ystr] - 1) * elast_c + (results_ref['eatr_c' + ystr] - results_base['eatr_c' + ystr]) * selast_c * mne_share_c)) maindata['deltaInc' + ystr] = ( ((results_ref['u_nc' + ystr] / results_base['u_nc' + ystr] - 1) * elast_nc + (results_ref['eatr_nc' + ystr] - results_base['eatr_nc' + ystr]) * selast_nc * mne_share_nc)) maindata['MPKc' + ystr] = (results_ref['u_c' + ystr] + results_base['u_c' + ystr]) / 2.0 maindata['MPKnc' + ystr] = (results_ref['u_nc' + ystr] + results_base['u_nc' + ystr]) / 2.0 # Save the responses self.investment_response = copy.deepcopy(maindata)
def calc_inv_response(self): """ Calculates the percent change in investment & marginal product of capital, for each asset type, for each year, corporate and noncorporate. firstyear: when the firm behavioral response takes effect """ # Read in the underlying functions for the investment response maindata = copy.deepcopy(Data().assets_data()) maindata.drop(['assets_c', 'assets_nc'], axis=1, inplace=True) # Extract relevant response parameters firstyear = self.elast_dict['first_year_response'] elast_c = self.elast_dict['inv_usercost_c'] elast_nc = self.elast_dict['inv_usercost_nc'] selast_c = self.elast_dict['inv_eatr_c'] selast_nc = self.elast_dict['inv_eatr_nc'] mne_share_c = self.elast_dict['mne_share_c'] mne_share_nc = self.elast_dict['mne_share_nc'] # No responses for years before first_year_response for year in range(2014, firstyear): maindata['deltaIc' + str(year)] = 0. maindata['deltaInc' + str(year)] = 0. maindata['MPKc' + str(year)] = 0. maindata['MPKnc' + str(year)] = 0. # Calculate cost of capital and EATR for every year for baseline Btax_base = BtaxMini(self.btax_params_base) results_base = Btax_base.run_btax_mini(range(firstyear, 2028)) # Calculate cost of capital and EATR for every year for reform Btax_ref = BtaxMini(self.btax_params_ref) results_ref = Btax_ref.run_btax_mini(range(firstyear, 2028)) # Compare results to produce the responses for year in range(firstyear, 2028): maindata['deltaIc' + str(year)] = ( (results_ref['u_c' + str(year)] / results_base['u_c' + str(year)] - 1) * elast_c + (results_ref['eatr_c' + str(year)] - results_base['eatr_c' + str(year)]) * selast_c * mne_share_c) maindata['deltaInc' + str(year)] = ( (results_ref['u_nc' + str(year)] / results_base['u_nc' + str(year)] - 1) * elast_nc + (results_ref['eatr_nc' + str(year)] - results_base['eatr_nc' + str(year)]) * selast_nc * mne_share_nc) maindata['MPKc' + str(year)] = (results_ref['u_c' + str(year)] + results_base['u_c' + str(year)]) / 2.0 maindata['MPKnc' + str(year)] = (results_ref['u_nc' + str(year)] + results_base['u_nc' + str(year)]) / 2.0 # Save the responses self.investment_response = copy.deepcopy(maindata)
def __init__(self, btax_params, data=None): # Store policy parameter objects if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') # Create Data object if isinstance(data, Data): self.data = data else: self.data = Data() # Extract baseline forecast for earnings and action self.cfc_data = copy.deepcopy(self.data.cfc_data) self.cfc_data.set_index('Unnamed: 0', inplace=True) # Generate earnings self.create_earnings()
def __init__(self, btax_params, data=None): # Store policy parameter object if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') # Create Data object if data is None: self.data = Data() elif isinstance(data, Data): self.data = data else: raise ValueError('data must be a Data object') # Extract baseline forecast for earnings and action self.dmne_data = copy.deepcopy(self.data.dmne_data) # Create affiliated CFC self.cfc = CFC(self.btax_params) # For initial creation, set includes_response to False self.includes_response = False
def __init__(self, btax_params, asset_forecast, data=None, response=None, eta=0.4, corp=True): # Create an associated Data object if isinstance(data, Data): self.data = data else: self.data = Data() if isinstance(corp, bool): self.corp = corp else: raise ValueError('corp must be True or False') if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') if response is not None: if len(response) == 14: self.response = response else: raise ValueError('Wrong response') else: self.response = np.zeros(14) if corp: self.delta = np.array( self.data.econ_defaults['f_c']) * (1 + self.response) else: self.delta = np.array( self.data.econ_defaults['f_nc']) * (1 + self.response) if len(asset_forecast) == 14: self.asset_forecast = asset_forecast else: raise ValueError('Wrong length for asset forecast') if eta >= 0 and eta <= 1: self.eta = eta else: raise ValueError('Value of eta inappropriate')
def run_btax_mini(self, yearlist): """ Runs the code to compute the user cost and EATR for each asset type for each year in yearlist. """ basedata = copy.deepcopy(Data().assets_data()) for year in yearlist: # Get calculations for each year results_oneyear = self.calc_oneyear(year) # Rename to include the year calculated results_oneyear.rename(columns={ 'uc_c': 'u_c' + str(year), 'uc_nc': 'u_nc' + str(year), 'eatr_c': 'eatr_c' + str(year), 'eatr_nc': 'eatr_nc' + str(year) }, inplace=True) # Merge year's results into combined DataFrame basedata = basedata.merge(right=results_oneyear, how='outer', on='Asset') basedata.drop(['assets_c', 'assets_nc'], axis=1, inplace=True) return basedata
def _calc_repatriation_response(self, btax_params_base, btax_params_ref): """ Calculates the change in the repatriation rate of current CFC after-tax profits. The parameter used is the semi-elasticity of repatriations with respect to the tax penalty from repatriating. The response is only used for repatriations from current profits, as repatriations from accumulated profits are too complicated to model explicity and not relevant following the 2017 tax act. Note that although the set-up for this may seem odd, it is designed to ensure that no policy change results in to repatriation response, regardless of the semielasticity used. The appropriate value for the semi-elasticity is -10.66536949, which is consistent with the repatriation rate as of 2014. """ # Get foreign tax rate ftax = Data().cfc_data.loc[0, 'taxrt'] # Get domestic tax rate dtax_base = np.asarray(btax_params_base['tau_c']) dtax_ref = np.asarray(btax_params_ref['tau_c']) # Get foreign dividend inclusion rate for CFCs divrt_base = np.asarray(btax_params_base['foreign_dividend_inclusion']) divrt_ref = np.asarray(btax_params_ref['foreign_dividend_inclusion']) penalty_base = np.maximum(dtax_base - ftax, 0.) * divrt_base penalty_ref = np.maximum(dtax_ref - ftax, 0.) * divrt_ref # Compute change in repatriation rate reprate_ch1 = (penalty_ref - penalty_base) * self.elasticities['reprate_inc'] reprate_ch = np.zeros(NUM_YEARS) for i in range(NUM_YEARS): if i + 2014 >= self.elasticities['first_year_response']: reprate_ch[i] = reprate_ch1[i] repat_response = pd.DataFrame({ 'year': range(START_YEAR, END_YEAR + 1), 'reprate_e': reprate_ch, 'reprate_a': np.zeros(NUM_YEARS) }) self.repatriation_response = repat_response
def calcIDAdjustment(corp, eta=0.4): """ Calculates the adjustment factors for the corporate and noncorporate debt and interest. eta: retirement rate of existing debt """ data = Data() policy = Policy() policy_params_df = policy.parameters_dataframe() # Create Asset object asset = Asset(policy_params_df, corp) asset.calc_all() # Get asset forecast forecast = asset.get_forecast() # Create Debt object debt = Debt(policy_params_df, forecast, corp=corp) debt.calc_all() # Get unscaled interest incomes and expenses intpaid_model = debt.int_expense[40:54] intinc_model = debt.int_income[40:54] muniinc_model = debt.muni_income[40:54] if corp: # Exclude anomalous results for 2007 paid_scale = (sum(intpaid[:7] / intpaid_model[:7]) + sum(intpaid[8:] / intpaid_model[8:])) / 13. inc_scale = (sum(taxint[:7] / intinc_model[:7]) + sum(taxint[8:] / intinc_model[8:])) / 13. muni_scale = (sum(ntaxint[:7] / muniinc_model[:7]) + sum(ntaxint[8:] / muniinc_model[8:]))/ 13. scales = [paid_scale, inc_scale, muni_scale] else: ID_irs = np.array(data.debt_data_noncorp['ID_Scorp'][40:54] + data.debt_data_noncorp['ID_sp'][40:54] + data.debt_data_noncorp['ID_partner'][40:54]) scales = sum(ID_irs / intpaid_model) / 14. assets14 = forecast[0] return (scales, assets14, intpaid_model, intinc_model, muniinc_model)
class Asset(): """ Constructor for the Asset class. This class includes several objects related to assets and depreciation: For internal class use: investment_history: array of investment amounts asset type (95) x year investment made (68) capital_history: array of asset amounts asset type (95) x years in the budget window (NUM_YEARS) system_history: array of depreciation systems (GDS or ADS) asset type (95) x year investment made (68) method_history: array of depreciation methods (DB, Economics, etc.) asset type (95) x year investment made (68) life_history: array of tax lives (see LIVES list) asset type (95) and year investment made (68) given system bonus_history: array of effective bonus depreciation rates asset type (95) x year investment made (68) ccr_data: DataFrame of asset amounts and economic depreciation rates asset type (95), 2017 only capital_path: DataFrame of asset information totals in the budget window Parameters: corp: True for corporate, False for noncorporate btax_params: dict of business tax policy parameters response: DataFrame of investment responses """ def __init__(self, btax_params, corp=True, data=None, response=None, industry='ALL'): # Create an associated Data object if isinstance(data, Data): self.data = data else: self.data = Data() # Check inputs if isinstance(corp, bool): self.corp = corp else: raise ValueError('corp must be True or False') if response is None or isinstance(response, pd.DataFrame): self.response = response else: raise ValueError('response must be DataFrame or None') if corp: self.adjustments = { 'bonus': 0.60290131, 'sec179': 0.016687178, 'rescalar': self.data.rescale_corp } else: self.adjustments = { 'bonus': 0.453683778, 'sec179': 0.17299506, 'rescalar': self.data.rescale_noncorp } if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') self.industry = industry def update_response(self, response): """ Updates the response DataFrame. Note: The response is the investment response DataFrame, not a Response object. """ assert isinstance(response, pd.DataFrame) self.response = response def build_inv_matrix(self): """ Builds investment array by asset type and by year made """ # Get historical investment for 1960-2014 if self.corp: investment_dfg = copy.deepcopy(self.data.investment_corp) else: investment_dfg = copy.deepcopy(self.data.investment_noncorp) investment_df = investment_dfg.loc[investment_dfg.industry == self.industry, :] investment_df.drop(['industry'], axis=1, inplace=True) investment_df.reset_index(drop=True, inplace=True) # Extend investment using NGDP (growth factors from CBO forecast) inv2014 = np.asarray(investment_df.loc[:, str(START_YEAR)]) for year in range(START_YEAR + 1, END_YEAR + 1): gfact1 = ( self.data.gfactors.loc[year - START_YEAR + 1, 'fi_nonres'] / self.data.gfactors.loc[1, 'fi_nonres']) gfact2 = (self.data.gfactors.loc[year - START_YEAR + 1, 'fi_res'] / self.data.gfactors.loc[1, 'fi_res']) investment_df.loc[:, str(year)] = inv2014 * gfact1 # Use residential investment gfactor for residential inv investment_df.loc[91:, str(year)] = inv2014[91:] * gfact2 # Update investment matrix to include investment responses if self.response is not None: if self.corp: deltaIkey = 'deltaIc' else: deltaIkey = 'deltaInc' for year in range(START_YEAR, END_YEAR + 1): deltaI = np.asarray(self.response[deltaIkey + str(year)]) investment_df.loc[:, str(year)] = ( investment_df.loc[:, str(year)] * (1. + deltaI)) self.investment_history = investment_df def build_deprLaw_matrices(self): """ Builds the arrays for tax depreciation laws """ def taxdep_final(depr_methods, depr_bonuses, depr_file): """ Constructs the DataFrame of information for tax depreciation. Only relevant for years beginning with START_YEAR. Returns a DataFrame with: depreciation method, tax depreciation life, true depreciation rate, bonus depreciation rate. """ taxdep = copy.deepcopy(self.data.taxdep_info_gross(depr_file)) system = np.asarray(taxdep['System']) life = np.asarray(taxdep['L_gds']) # Determine depreciation systems for each asset class (by GDS life) for cl, method in depr_methods.items(): system[life == cl] = method # Determine tax life L_ads = np.asarray(taxdep['L_ads']) Llist = np.asarray(taxdep['L_gds']) Llist[system == 'ADS'] = L_ads[system == 'ADS'] Llist[system == 'None'] = 9e99 taxdep['L'] = Llist # Determine depreciation method. Default is GDS method method = np.asarray(taxdep['Method']) for i in range(len(method)): if system[i] == 'ADS': method[i] = np.asarray(taxdep['ADS method'])[i] elif system[i] == 'Economic': method[i] = 'Economic' elif system[i] == 'None': method[i] = 'None' elif system[i] == 'Expensing': method[i] = 'Expensing' elif system[i] != 'GDS': asset1 = np.asarray(taxdep['Asset'])[i] raise ValueError('Must specify depreciation system for ' + asset1 + '. Cannot use ' + str(system[i])) taxdep['Method'] = method # Detemine bonus depreciation rate bonus = np.zeros(len(taxdep)) for cl, cl_bonus in depr_bonuses.items(): bonus[life == cl] = cl_bonus taxdep['bonus'] = bonus taxdep.drop(['L_gds', 'L_ads', 'Class life'], axis=1, inplace=True) return taxdep def taxdep_preset(): """ Constructs the DataFrame of information for tax depreciation. Only relevant for years before START_YEAR. Returns a DataFrame with: depreciation method, tax depreciation life, true depreciation rate, bonus depreciation rate. """ taxdep = copy.deepcopy(self.data.taxdep_info_gross('pre2017')) taxdep['L'] = taxdep['L_gds'] life = np.asarray(taxdep['L_gds']) bonus = np.zeros(len(life)) for y in [3, 5, 7, 10, 15, 20, 25, 27.5, 39]: s = "bonus{}".format(y if y != 27.5 else 27) bonus[life == y] = self.data.bonus_data[s][year - HISTORY_START] taxdep['bonus'] = bonus taxdep.drop(['L_gds', 'L_ads', 'Class life'], axis=1, inplace=True) return taxdep def get_btax_params_oneyear(btax_params, year): """ Extracts tax depreciation parameters and calls the functions to build the tax depreciation DataFrames. """ if year >= START_YEAR: year = min(year, END_YEAR) iyr = year - START_YEAR depr_methods = {} depr_bonuses = {} for y in [3, 5, 7, 10, 15, 20, 25, 27.5, 39]: s = "depr_{}yr_".format(y if y != 27.5 else 275) depr_methods[y] = btax_params[s + 'method'][iyr] depr_bonuses[y] = btax_params[s + 'bonus'][iyr] depr_file = btax_params.loc[year - START_YEAR, 'depr_file'] taxdep = taxdep_final(depr_methods, depr_bonuses, depr_file) else: taxdep = taxdep_preset() return taxdep """ Create arrays and store depreciation rules in them """ length1 = END_YEAR - HISTORY_START + 1 method_history = [[]] * length1 life_history = np.zeros((95, length1)) bonus_history = np.zeros((95, length1)) for year in range(HISTORY_START, END_YEAR + 1): iyr = year - HISTORY_START params_oneyear = get_btax_params_oneyear(self.btax_params, year) method_history[iyr] = params_oneyear['Method'] life_history[:, iyr] = params_oneyear['L'] bonus_history[:, iyr] = params_oneyear['bonus'] self.method_history = method_history self.life_history = life_history self.bonus_history = bonus_history def calcDep_oneyear(self, year): """ Calculates total depreciation deductions taken in the year. """ def depreciationDeduction(year_investment, year_deduction, method, L, delta, bonus): """ Computes the nominal depreciation deduction taken on any unit investment in any year with any depreciation method and life. Parameters: year_investment: year the investment is made year_deduction: year the CCR deduction is taken method: method of CCR (DB 200%, DB 150%, SL, Expensing, None) L: class life for DB or SL depreciation (MACRS) delta: economic depreciation rate bonus: bonus depreciation rate """ assert method in [ 'DB 200%', 'DB 150%', 'SL', 'Expensing', 'Economic', 'None' ] # No depreciation if method == 'None': deduction = 0 # Expensing elif method == 'Expensing': if year_deduction == year_investment: deduction = 1.0 else: deduction = 0 # Economic depreciation elif method == 'Economic': yded = year_deduction pi_temp = (self.data.investmentGfactors_data['pce'][yded + 1] / self.data.investmentGfactors_data['pce'][yded]) if pi_temp == np.exp(delta): annual_change = 1.0 else: if year_deduction == year_investment: annual_change = (( (pi_temp * np.exp(delta / 2))**0.5 - 1) / (np.log(pi_temp - delta))) else: annual_change = ((pi_temp * np.exp(delta) - 1) / (np.log(pi_temp) - delta)) if year_deduction < year_investment: sval = 0 deduction = 0 elif year_deduction == year_investment: sval = 1.0 deduction = (bonus + (1 - bonus) * delta * sval * annual_change) else: sval = ( np.exp(-delta * (year_deduction - year_investment)) * self.data.investmentGfactors_data['pce'] [year_deduction] / 2.0 / (self.data.investmentGfactors_data['pce'] [year_investment] + self.data. investmentGfactors_data['pce'][year_investment + 1])) deduction = (1 - bonus) * delta * sval * annual_change else: if method == 'DB 200%': N = 2 elif method == 'DB 150%': N = 1.5 elif method == 'SL': N = 1 # DB or SL depreciation, half-year convention t0 = year_investment + 0.5 t1 = t0 + L * (1 - 1 / N) s1 = year_deduction s2 = s1 + 1 if year_deduction < year_investment: deduction = 0 elif year_deduction > year_investment + L: deduction = 0 elif year_deduction == year_investment: deduction = (bonus + (1 - bonus) * (1 - np.exp(-N / L * 0.5))) elif s2 <= t1: deduction = ((1 - bonus) * (np.exp(-N / L * (s1 - t0)) - np.exp(-N / L * (s2 - t0)))) elif s1 >= t1 and s1 <= t0 + L and s2 > t0 + L: deduction = ((1 - bonus) * (N / L * np.exp(1 - N) * (s2 - s1) * 0.5)) elif s1 >= t1 and s2 <= t0 + L: deduction = ((1 - bonus) * (N / L * np.exp(1 - N) * (s2 - s1))) elif s1 < t1 and s2 > t1: deduction = ((1 - bonus) * (np.exp(-N / L * (s1 - t0)) - np.exp(-N / L * (t1 - t0)) + N / L * np.exp(1 - N) * (s2 - t1))) return deduction """ Calculate depreciation deductions for each year """ unitDep_arr = np.zeros((95, END_YEAR - HISTORY_START + 1)) depr_file = self.btax_params.loc[year - START_YEAR, 'depr_file'] delta = np.asarray(self.data.taxdep_info_gross(depr_file)['delta']) for i in range(95): # Iterate over asset types for j in range(END_YEAR - HISTORY_START + 1): # Iterate over investment years bonus1 = min( (self.bonus_history[i, j] * self.adjustments['bonus'] + self.adjustments['sec179']), 1.0) unitDep_arr[i, j] = depreciationDeduction( j, year - HISTORY_START, self.method_history[j][i], self.life_history[i, j], delta[i], bonus1) inv_hist = copy.deepcopy(self.investment_history) inv_hist.drop(['asset_code'], axis=1, inplace=True) inv_hist2 = inv_hist.to_numpy() Dep_arr = inv_hist2 * unitDep_arr # Apply the haircut on undepreciated basis iyr = year - START_YEAR if year < START_YEAR: # Use no haircut for years before calculator hc_undep_year = 0 hc_undep = 0. else: if self.corp: hc_undep_year = np.array( self.btax_params['undepBasis_corp_hcyear'])[iyr] hc_undep = np.array( self.btax_params['undepBasis_corp_hc'])[iyr] else: hc_undep_year = np.array( self.btax_params['undepBasis_noncorp_hcyear'])[iyr] hc_undep = np.array( self.btax_params['undepBasis_noncorp_hc'])[iyr] if year >= hc_undep_year: for j in range(END_YEAR - HISTORY_START + 1): if j < hc_undep_year: Dep_arr[:, j] = Dep_arr[:, j] * (1 - hc_undep) # Asset types included in tax depreciation or not other_assets = [68, 69, 70] # Software other_assets.extend(range(71, 86)) # Add R&D categories depr_assets = list(range(0, 68)) # Tangible assets depr_assets.extend(range(86, 95)) # Add artistic originals depr_assets.append(93) # Add residential # Tax depreciation deduction depded = Dep_arr[depr_assets, :].sum().sum() # Other CCR deduction otherded = Dep_arr[other_assets, :].sum().sum() return [depded, otherded] def calcDep_allyears(self): """ Calculates total depreciation deductions taken for all years 1960-2035. """ dep_deductions = np.zeros(END_YEAR + 1 - HISTORY_START) for year in range(HISTORY_START, END_YEAR + 1): dep_deductions[year - HISTORY_START] = self.calcDep_oneyear(year)[0] return dep_deductions def calcDep_budget(self): """ Calculates total depreciation deductions taken for START_ to END_YEAR. """ dep_deductions = np.zeros(NUM_YEARS) other_deductions = np.zeros(NUM_YEARS) for iyr in range(0, NUM_YEARS): year = iyr + START_YEAR [depded, otherded] = self.calcDep_oneyear(year) dep_deductions[iyr] = depded other_deductions[iyr] = otherded return dep_deductions def build_capital_history(self): """ Builds capital history array using investment_history and capital_df """ # Get historical capital stock if self.corp: capital_dfg = copy.deepcopy(self.data.capital_corp) else: capital_dfg = copy.deepcopy(self.data.capital_noncorp) capital_df1 = capital_dfg[capital_dfg.industry == self.industry] capital_df1.drop(['industry'], axis=1, inplace=True) capital_df1.reset_index(drop=True, inplace=True) capital_df1.rename(columns={'asset_code': 'Code'}, inplace=True) capital_df2 = capital_df1.merge(right=self.data.econ_depr_df(), how='outer', on='Code') trueDep_df = copy.deepcopy(self.data.econ_depr_df()) pcelist = np.asarray(self.data.investmentGfactors_data['pce']) for year in range(START_YEAR, END_YEAR + 1): trueDep_df[str( year)] = capital_df2[str(year)] * trueDep_df['delta'] capital_df2[str(year + 1)] = ( (capital_df2[str(year)] - trueDep_df[str(year)] + self.investment_history[str(year)]) * pcelist[year - HISTORY_START + 1] / pcelist[year - HISTORY_START]) self.capital_history = capital_df2 self.trueDep = trueDep_df def build_capital_path(self): """ Builds the DataFrame of asset amount, investment and depreciation totals for each year in the budget window. """ # Sum across assets and put into new dataset Kstock_total = np.zeros(NUM_YEARS) trueDep_total = np.zeros(NUM_YEARS) inv_total = np.zeros(NUM_YEARS) Mdep_total = np.zeros(NUM_YEARS) Oded_total = np.zeros(NUM_YEARS) for year in range(START_YEAR, END_YEAR + 1): iyr = year - START_YEAR adjfactor = self.adjustments['rescalar'][iyr] Kstock_total[iyr] = sum( self.capital_history[str(year)]) * adjfactor trueDep_total[iyr] = sum(self.trueDep[str(year)]) * adjfactor inv_total[iyr] = sum( self.investment_history[str(year)]) * adjfactor [depded, otherded] = self.calcDep_oneyear(year) Mdep_total[iyr] = depded * adjfactor Oded_total[iyr] = otherded * adjfactor cap_result = pd.DataFrame({ 'year': range(START_YEAR, END_YEAR + 1), 'Kstock': Kstock_total, 'Investment': inv_total, 'trueDep': trueDep_total, 'taxDep': Mdep_total, 'otherCCR': Oded_total }) self.capital_path = cap_result def calc_all(self): """ Executes all calculations for Asset object. """ self.build_inv_matrix() self.build_deprLaw_matrices() self.build_capital_history() self.build_capital_path() return None def get_forecast(self): """ Returns an array of the capital stock for [START_YEAR, END_YEAR] """ forecast = np.array(self.capital_path['Kstock']) return forecast def get_taxdep(self): """ Returns an array of tax depreciation deductions for [START_YEAR, END_YEAR] """ taxdep = np.array(self.capital_path['taxDep']) return taxdep def get_investment(self): """ Returns an array of total investment for [START_YEAR, END_YEAR] """ inv1 = np.array(self.capital_path['Investment']) return inv1 def get_truedep(self): """ Returns an array of true depreciation for [START_YEAR, END_YEAR] """ truedep = np.array(self.capital_path['trueDep']) return truedep
def calc_oneyear(self, year): """ In the given year, calculates EATR and user cost of capital for each asset type. """ # Check that year has acceptable value assert year in range(2017, 2028) # Extract economic parameters [r_c, r_nc, r_d, pi, f_c, f_nc] = self.get_econ_params_oneyear(year) # Extract tax depreciation information Method = self.asset_c.method_history[year - 1960] Life = self.asset_c.life_history[:, year - 1960] Bonus = self.asset_c.bonus_history[:, year - 1960] # Make tax rate dictionaries tdict_c = self.make_tdict_c(year) tdict_nc = self.make_tdict_nc(year) # Create base DataFrame and get depreciation rates asset_data = copy.deepcopy(Data().assets_data()) Delta = np.array(Data().econ_depr_df()['delta']) # Get deductible fractions of interest paid (fracded_c, fracded_nc) = self.calc_frac_ded(year) # Get inventory method inv_method = self.btax_params['inventory_method'][year - 2014] assets = np.asarray(asset_data['Asset']) uc_c = np.zeros(len(assets)) uc_nc = np.zeros(len(assets)) eatr_c = np.zeros(len(assets)) eatr_nc = np.zeros(len(assets)) for j in range(len(asset_data)): uc_c[j] = self.calc_usercost(r_c, pi, Delta[j], Method[j], Life[j], Bonus[j], f_c, r_d, fracded_c, tdict_c, 50) uc_nc[j] = self.calc_usercost(r_nc, pi, Delta[j], Method[j], Life[j], Bonus[j], f_nc, r_d, fracded_nc, tdict_nc, 50) eatr_c[j] = self.calc_eatr(0.2, r_c, pi, Delta[j], Method[j], Life[j], Bonus[j], f_c, r_d, fracded_c, tdict_c, length=50) eatr_nc[j] = self.calc_eatr(0.2, r_nc, pi, Delta[j], Method[j], Life[j], Bonus[j], f_nc, r_d, fracded_nc, tdict_nc, length=50) # Special cost of capital calculations for inventories uc_c[assets == 'Inventories'] = self.calc_rho_inv( r_c, pi, inv_method, 0.5, tdict_c) uc_nc[assets == 'Inventories'] = self.calc_rho_inv( r_nc, pi, inv_method, 0.5, tdict_nc) # EATR for inventories and land with no supernormal returns eatr_c[assets == 'Inventories'] = (uc_c[assets == 'Inventories'] - r_c) / uc_c[assets == 'Inventories'] eatr_nc[assets == 'Inventories'] = (uc_nc[assets == 'Inventories'] - r_nc) / uc_nc[assets == 'Inventories'] eatr_c[assets == 'Land'] = (uc_c[assets == 'Land'] - r_c) / uc_c[assets == 'Land'] eatr_nc[assets == 'Land'] = (uc_nc[assets == 'Land'] - r_nc) / uc_nc[assets == 'Land'] # Save the results to the main DataFrame asset_data['uc_c'] = uc_c asset_data['uc_nc'] = uc_nc asset_data['eatr_c'] = eatr_c asset_data['eatr_nc'] = eatr_nc asset_data.drop(['assets_c', 'assets_nc'], axis=1, inplace=True) return asset_data
def __init__(self, btax_params): self.econ_params = copy.deepcopy(Data().econ_defaults) self.btax_params = btax_params self.asset_c = Asset(btax_params, corp=True) self.asset_c.build_deprLaw_matrices()
class Corporation(): """ Constructor for the Corporation class. This contains both the real and tax information relevant to the corporate income tax. """ def __init__(self, btax_params): # Store policy parameter objects if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') # Create Data object self.data = Data() # Create Asset object and calculate self.asset = Asset(self.btax_params, corp=True, data=self.data) self.asset.calc_all() # Create DomesticMNE object self.dmne = DomesticMNE(self.btax_params) self.dmne.calc_all() # Create earnings forecast self.create_earnings() def create_debt(self): """ Creates the Debt object for the Corporation. Note: create_asset must have already been called """ self.debt = Debt(self.btax_params, self.asset.get_forecast(), data=self.data, corp=True) self.debt.calc_all() def create_earnings(self): """ Creates the initial forecast for earnings. Static only. """ # Grab forecasts of profit growth earnings_forecast = np.asarray(self.data.gfactors['profit']) gfacts = earnings_forecast[1:] / earnings_forecast[0] # 2013 values for non-modeled revenues taxitems = np.array(self.data.corp_tax2013['ALL']) receipts = taxitems[4] * gfacts rent_inc = taxitems[7] * gfacts royalties = taxitems[8] * gfacts capgains = (taxitems[9] + taxitems[10] + taxitems[11] - taxitems[32]) * gfacts domestic_divs = taxitems[12] * gfacts other_recs = taxitems[14] * gfacts # 2013 values for non-modeled deductions and credits cogs = taxitems[16] * gfacts execcomp = taxitems[17] * gfacts wages = taxitems[18] * gfacts repairs = taxitems[19] * gfacts baddebt = taxitems[20] * gfacts rent_paid = taxitems[21] * gfacts statelocaltax = taxitems[22] * gfacts charity = taxitems[24] * gfacts amortization = taxitems[25] * gfacts depletion = taxitems[27] * gfacts advertising = taxitems[28] * gfacts pensions = taxitems[29] * gfacts benefits = taxitems[30] * gfacts sec199_base = taxitems[31] * gfacts other_ded = taxitems[33] * gfacts gbc = taxitems[42] * gfacts # Save unodeled tax items self.revenues = pd.DataFrame({ 'year': range(START_YEAR, END_YEAR + 1), 'receipts': receipts, 'rent': rent_inc, 'royalties': royalties, 'capgains': capgains, 'domestic_divs': domestic_divs, 'other': other_recs }) self.deductions = pd.DataFrame({ 'year': range(START_YEAR, END_YEAR + 1), 'cogs': cogs, 'execcomp': execcomp, 'wages': wages, 'repairs': repairs, 'baddebt': baddebt, 'rent': rent_paid, 'statelocaltax': statelocaltax, 'charity': charity, 'amortization': amortization, 'depletion': depletion, 'advertising': advertising, 'pensions': pensions, 'benefits': benefits, 'sec199share': sec199_base, 'other': other_ded }) self.credits = pd.DataFrame({ 'year': range(START_YEAR, END_YEAR + 1), 'gbc': gbc }) def file_taxes(self): """ Creates the CorpTaxReturn object. """ self.taxreturn = CorpTaxReturn(self.btax_params, self.revenues, self.deductions, self.credits, dmne=self.dmne, data=self.data, assets=self.asset, debts=self.debt) self.taxreturn.calc_all() def real_activity(self): """ Produces a DataFrame of the corporation's real activity. Real measures are: Capital stock Investment Depreciation (economic) Net debt Net interest paid Earnings Tax liability Net income Cash flow """ real_results = pd.DataFrame({'year': range(START_YEAR, END_YEAR + 1)}) real_results['Earnings'] = ( self.revenues['receipts'] + self.revenues['rent'] + self.revenues['royalties'] + self.revenues['capgains'] + self.revenues['domestic_divs'] + self.revenues['other'] - self.deductions['cogs'] - self.deductions['execcomp'] - self.deductions['wages'] - self.deductions['repairs'] - self.deductions['baddebt'] - self.deductions['rent'] - self.deductions['charity'] - self.deductions['depletion'] - self.deductions['advertising'] - self.deductions['pensions'] - self.deductions['benefits'] - self.deductions['other'] + self.dmne.dmne_results['foreign_directinc'] + self.dmne.dmne_results['foreign_indirectinc']) real_results['Kstock'] = self.asset.get_forecast() real_results['Inv'] = self.asset.get_investment() real_results['Depr'] = self.asset.get_truedep() real_results['Debt'] = self.debt.get_debt() real_results['NIP'] = self.debt.get_nip() real_results['Tax'] = self.taxreturn.get_tax() real_results['NetInc'] = (real_results['Earnings'] - real_results['Depr'] - real_results['NIP'] - real_results['Tax'] - self.dmne.dmne_results['foreign_tax'] - self.deductions['statelocaltax']) real_results['CashFlow'] = (real_results['Earnings'] - real_results['Inv'] - real_results['Tax'] - self.dmne.dmne_results['foreign_tax'] - self.deductions['statelocaltax']) self.real_results = real_results def calc_static(self): """ Runs the static calculations. """ self.create_debt() self.file_taxes() self.real_activity() def update_legal(self, responses): """ Updates the rescale_corp and rescale_noncorp associated with each Data associated with each object. """ self.data.update_rescaling(responses.rescale_corp, responses.rescale_noncorp) self.asset.data.update_rescaling(responses.rescale_corp, responses.rescale_noncorp) def update_investment(self, responses): """ Updates the Asset object to include investment response. """ # First, save the capital stock by asset type and year (for earnings) self.old_capital_history = copy.deepcopy(self.asset.capital_history) self.asset.update_response(responses.investment_response) self.asset.calc_all() def update_repatriation(self, responses): """ Updates the DomesticMNE object to include the repatriation response. Also updates profits to reflect this response. """ # First, save current foreign earnings self.dmne.update_profits(responses.repatriation_response, responses.shifting_response) self.dmne.calc_all() def update_earnings(self, responses): """ Recalculates earnings using the old capital stock by asset type, the new capital stock by asset type (based on the investment response), and the marginal product of capital. """ Kstock_base = copy.deepcopy(self.old_capital_history) Kstock_ref = copy.deepcopy(self.asset.capital_history) changeEarnings = np.zeros((95, NUM_YEARS)) for iyr in range(NUM_YEARS): # for each year ystr = str(iyr + START_YEAR) mpk = np.array(responses.investment_response['MPKc' + ystr]) for i in range(95): # by asset changeEarnings[i, iyr] = (Kstock_ref[ystr][i] - Kstock_base[ystr][i]) * mpk[i] deltaE = np.zeros(NUM_YEARS) for iyr in range(NUM_YEARS): deltaE[iyr] = changeEarnings[:, iyr].sum() # Update new earnings self.revenues['receipts'] = self.revenues['receipts'] + deltaE def update_debt(self, responses): """ Replaces the Debt object to use the new asset forecast and Data """ pctch_delta = np.array(responses.debt_response['pchDelta_corp']) self.debt = Debt(self.btax_params, self.asset.get_forecast(), data=self.data, response=pctch_delta, corp=True) self.debt.calc_all() def apply_responses(self, responses): """ Updates Data, Asset, earnings, Debt and CorpTaxReturn to include responses. Then calc_all() for each object. """ assert isinstance(responses, Response) self.update_legal(responses) self.update_investment(responses) self.update_repatriation(responses) self.update_earnings(responses) self.update_debt(responses) self.file_taxes() self.real_activity() def get_netinc(self): """ Returns an array of the corporation's net income (after-tax). """ netinc = np.array(self.real_results['NetInc']) return netinc def get_taxrev(self): """ Returns an array of the corporation's tax liability. """ taxrev = np.array(self.real_results['Tax']) return taxrev
We need to produce the following objects: adjfactors.csv pass-through shares """ import copy import numpy as np import pandas as pd import scipy.optimize from biztax.data import Data from biztax.asset import Asset from biztax.debt import Debt from biztax.policy import Policy # Specify single Data object (for convenience) data1 = Data() """ Section 1. Calculation of the adjustment parameters """ def calcAMTparams2(): """ Calculates the adjustment factors for the AMT and PYMTC """ # Grab historical data hist_data = copy.deepcopy(data1.historical_combined) taxinc = np.array(hist_data['taxinc']) amt = np.array(hist_data['amt']) stock13 = 26.0 A13 = 4.196871
class Asset(): """ Constructor for the Asset class. This class includes several objects related to assets and depreciation: For internal class use: investment_history: array of investment amounts asset type (96) x year investment made (75) capital_history: array of asset amounts asset type (96) x years in the budget window (14) system_history: array of depreciation systems (GDS or ADS) asset type (96) x year investment made (75) method_history: array of depreciation methods (DB, Economics, etc.) asset type (96) x year investment made (75) life_history: array of tax lives (see LIVES list) asset type (96) and year investment made (75) given system bonus_history: array of effective bonus depreciation rates asset type (96) x year investment made (75) ccr_data: DataFrame of asset amounts and economic depreciation rates asset type (96), 2017 only capital_path: DataFrame of asset information totals in the budget window Parameters: corp: True for corporate, False for noncorporate btax_params: dict of business tax policy parameters response: DataFrame of investment responses """ def __init__(self, btax_params, corp=True, data=None, response=None): # Create an associated Data object if isinstance(data, Data): self.data = data else: self.data = Data() # Check inputs if isinstance(corp, bool): self.corp = corp else: raise ValueError('corp must be True or False') if response is None or isinstance(response, pd.DataFrame): self.response = response else: raise ValueError('response must be DataFrame or None') if corp: self.adjustments = { 'bonus': 0.60290131, 'sec179': 0.016687178, 'overall': self.data.adjfactor_dep_corp, 'rescalar': self.data.rescale_corp } else: self.adjustments = { 'bonus': 0.453683778, 'sec179': 0.17299506, 'overall': self.data.adjfactor_dep_noncorp, 'rescalar': self.data.rescale_noncorp } if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') def update_response(self, response): """ Updates the response DataFrame. Note: The response is the investment response DataFrame, not a Response object. """ assert isinstance(response, pd.DataFrame) self.response = response def get_ccr_data(self): assets2017 = copy.deepcopy(self.data.assets_data()) ccrdata = assets2017.merge(right=self.data.econ_depr_df(), how='outer', on='Asset') self.ccr_data = ccrdata def build_inv_matrix(self): """ Builds investment array by asset type and by year made """ inv_mat1 = np.zeros((96, 75)) # Build historical portion for j in range(57): if self.corp: inv_mat1[:, j] = ( self.data.investmentrate_data['i' + str(j + 1960)] * self.data.investmentshare_data['c_share'][j]) else: inv_mat1[:, j] = ( self.data.investmentrate_data['i' + str(j + 1960)] * (1 - self.data.investmentshare_data['c_share'][j])) # Extend investment using NGDP (growth factors from CBO forecast) for j in range(57, 75): inv_mat1[:, j] = (inv_mat1[:, 56] * self.data.investmentGfactors_data['ngdp'][j] / self.data.investmentGfactors_data['ngdp'][56]) # Investment forecast for 2017 if self.corp: inv2017 = np.asarray( self.ccr_data['assets_c'] * (self.data.investmentGfactors_data['ngdp'][57] / self.data.investmentGfactors_data['ngdp'][56] - 1 + self.ccr_data['delta'])) else: inv2017 = np.asarray( self.ccr_data['assets_nc'] * (self.data.investmentGfactors_data['ngdp'][57] / self.data.investmentGfactors_data['ngdp'][56] - 1 + self.ccr_data['delta'])) # Rescale to match B-Tax data for 2017 inv_mat2 = np.zeros((96, 75)) l1 = list(range(96)) l1.remove(32) # exclude land for j in range(75): for i in l1: inv_mat2[i, j] = inv_mat1[i, j] * inv2017[i] / inv_mat1[i, 57] # Update investment matrix to include investment responses inv_mat3 = copy.deepcopy(inv_mat2) if self.response is not None: if self.corp: deltaIkey = 'deltaIc' else: deltaIkey = 'deltaInc' for i in range(96): for j in range(57, 68): deltaI = self.response[deltaIkey + str(j + 1960)].tolist()[i] inv_mat3[i, j] = inv_mat2[i, j] * (1 + deltaI) self.investment_history = inv_mat3 def build_deprLaw_matrices(self): """ Builds the arrays for tax depreciation laws """ def taxdep_final(depr_methods, depr_bonuses): """ Constructs the DataFrame of information for tax depreciation. Only relevant for years beginning with 2014. Returns a DataFrame with: depreciation method, tax depreciation life, true depreciation rate, bonus depreciation rate. """ taxdep = copy.deepcopy(self.data.taxdep_info_gross()) system = np.empty(len(taxdep), dtype='S10') class_life = np.asarray(taxdep['GDS Class Life']) # Determine depreciation systems for each asset type for cl, method in depr_methods.items(): system[class_life == cl] = method # Determine tax life L_ads = np.asarray(taxdep['L_ads']) Llist = np.asarray(taxdep['L_gds']) Llist[system == 'ADS'] = L_ads[system == 'ADS'] Llist[system == 'None'] = 100 taxdep['L'] = Llist # Determine depreciation method. Default is GDS method method = np.asarray(taxdep['Method']) method[system == 'ADS'] = 'SL' method[system == 'Economic'] = 'Economic' method[system == 'None'] = 'None' taxdep['Method'] = method # Detemine bonus depreciation rate bonus = np.zeros(len(taxdep)) for cl, cl_bonus in depr_bonuses.items(): bonus[class_life == cl] = cl_bonus taxdep['bonus'] = bonus taxdep.drop(['L_gds', 'L_ads', 'GDS Class Life'], axis=1, inplace=True) return taxdep def taxdep_preset(year): """ Constructs the DataFrame of information for tax depreciation. Only relevant for years before 2014. Returns a DataFrame with: depreciation method, tax depreciation life, true depreciation rate, bonus depreciation rate. """ taxdep = copy.deepcopy(self.data.taxdep_info_gross()) taxdep['L'] = taxdep['L_gds'] class_life = np.asarray(taxdep['GDS Class Life']) bonus = np.zeros(len(class_life)) for y in [3, 5, 7, 10, 15, 20, 25, 27.5, 39]: s = "bonus{}".format(y if y != 27.5 else 27) bonus[class_life == y] = self.data.bonus_data[s][year - 1960] taxdep['bonus'] = bonus taxdep.drop(['L_gds', 'L_ads', 'GDS Class Life'], axis=1, inplace=True) return taxdep def get_btax_params_oneyear(btax_params, year): """ Extracts tax depreciation parameters and calls the functions to build the tax depreciation DataFrames. """ if year >= 2014: year = min(year, 2027) depr_methods = {} depr_bonuses = {} for y in [3, 5, 7, 10, 15, 20, 25, 27.5, 39]: s = "depr_{}yr_".format(y if y != 27.5 else 275) depr_methods[y] = btax_params[s + 'method'][year - 2014] depr_bonuses[y] = btax_params[s + 'bonus'][year - 2014] taxdep = taxdep_final(depr_methods, depr_bonuses) else: taxdep = taxdep_preset(year) return taxdep """ Create arrays and store depreciation rules in them """ method_history = [[]] * 75 life_history = np.zeros((96, 75)) bonus_history = np.zeros((96, 75)) for year in range(1960, 2035): params_oneyear = get_btax_params_oneyear(self.btax_params, year) method_history[year - 1960] = params_oneyear['Method'] life_history[:, year - 1960] = params_oneyear['L'] bonus_history[:, year - 1960] = params_oneyear['bonus'] self.method_history = method_history self.life_history = life_history self.bonus_history = bonus_history def calcDep_oneyear(self, year): """ Calculates total depreciation deductions taken in the year. """ def depreciationDeduction(year_investment, year_deduction, method, L, delta, bonus): """ Computes the nominal depreciation deduction taken on any unit investment in any year with any depreciation method and life. Parameters: year_investment: year the investment is made year_deduction: year the CCR deduction is taken method: method of CCR (DB 200%, DB 150%, SL, Expensing, None) L: class life for DB or SL depreciation (MACRS) delta: economic depreciation rate bonus: bonus depreciation rate """ assert method in [ 'DB 200%', 'DB 150%', 'SL', 'Expensing', 'Economic', 'None' ] # No depreciation if method == 'None': deduction = 0 # Expensing elif method == 'Expensing': if year_deduction == year_investment: deduction = 1.0 else: deduction = 0 # Economic depreciation elif method == 'Economic': pi_temp = ( self.data.investmentGfactors_data['pce'][year_deduction + 1] / self.data.investmentGfactors_data['pce'][year_deduction]) if pi_temp == np.exp(delta): annual_change = 1.0 else: if year_deduction == year_investment: annual_change = (( (pi_temp * np.exp(delta / 2))**0.5 - 1) / (np.log(pi_temp - delta))) else: annual_change = ((pi_temp * np.exp(delta) - 1) / (np.log(pi_temp) - delta)) if year_deduction < year_investment: sval = 0 deduction = 0 elif year_deduction == year_investment: sval = 1.0 deduction = bonus + (1 - bonus) * delta * sval * annual_change else: sval = ( np.exp(-delta * (year_deduction - year_investment)) * self.data.investmentGfactors_data['pce'] [year_deduction] / 2.0 / (self.data.investmentGfactors_data['pce'] [year_investment] + self.data. investmentGfactors_data['pce'][year_investment + 1])) deduction = (1 - bonus) * delta * sval * annual_change else: if method == 'DB 200%': N = 2 elif method == 'DB 150%': N = 1.5 elif method == 'SL': N = 1 # DB or SL depreciation, half-year convention t0 = year_investment + 0.5 t1 = t0 + L * (1 - 1 / N) s1 = year_deduction s2 = s1 + 1 if year_deduction < year_investment: deduction = 0 elif year_deduction > year_investment + L: deduction = 0 elif year_deduction == year_investment: deduction = bonus + (1 - bonus) * (1 - np.exp(-N / L * 0.5)) elif s2 <= t1: deduction = ((1 - bonus) * (np.exp(-N / L * (s1 - t0)) - np.exp(-N / L * (s2 - t0)))) elif s1 >= t1 and s1 <= t0 + L and s2 > t0 + L: deduction = (1 - bonus) * (N / L * np.exp(1 - N) * (s2 - s1) * 0.5) elif s1 >= t1 and s2 <= t0 + L: deduction = (1 - bonus) * (N / L * np.exp(1 - N) * (s2 - s1)) elif s1 < t1 and s2 > t1: deduction = ((1 - bonus) * (np.exp(-N / L * (s1 - t0)) - np.exp(-N / L * (t1 - t0)) + N / L * np.exp(1 - N) * (s2 - t1))) return deduction """ Calculate depreciation deductions for each year """ unitDep_arr = np.zeros((96, 75)) for i in range(96): # Iterate over asset types for j in range(75): # Iterate over investment years bonus1 = min( self.bonus_history[i, j] * self.adjustments['bonus'] + self.adjustments['sec179'], 1.0) unitDep_arr[i, j] = depreciationDeduction( j, year - 1960, self.method_history[j][i], self.life_history[i, j], self.ccr_data['delta'][i], bonus1) Dep_arr = self.investment_history * unitDep_arr # Apply the haircut on undepreciated basis if self.corp: hc_undep_year = np.array( self.btax_params['undepBasis_corp_hcyear'])[year - 2014] hc_undep = np.array(self.btax_params['undepBasis_corp_hc'])[year - 2014] else: hc_undep_year = np.array( self.btax_params['undepBasis_noncorp_hcyear'])[year - 2014] hc_undep = np.array( self.btax_params['undepBasis_noncorp_hc'])[year - 2014] if year >= hc_undep_year: for j in range(75): if j < hc_undep_year: Dep_arr[:, j] = Dep_arr[:, j] * (1 - hc_undep) # Calculate the total deduction total_depded = Dep_arr.sum().sum() return total_depded def calcDep_allyears(self): """ Calculates total depreciation deductions taken for all years 1960-2035. """ dep_deductions = np.zeros(75) for year in range(1960, 2035): dep_deductions[year - 1960] = self.calcDep_oneyear(year) return dep_deductions def calcDep_budget(self): """ Calculates total depreciation deductions taken for 2014 - 2027. """ dep_deductions = np.zeros(14) for year in range(2014, 2028): dep_deductions[year - 2014] = self.calcDep_oneyear(year) return dep_deductions def build_capital_history(self): """ Builds capital history array using investment_history and ccr_data """ Kstock = np.zeros((96, 15)) trueDep = np.zeros((96, 14)) pcelist = np.asarray(self.data.investmentGfactors_data['pce']) deltalist = np.asarray(self.ccr_data['delta']) for i in range(96): # Starting by assigning 2017 data from B-Tax if self.corp: Kstock[i, 3] = np.asarray(self.ccr_data['assets_c'])[i] else: Kstock[i, 3] = np.asarray(self.ccr_data['assets_nc'])[i] # Using 2017 asset totals, apply retroactively for j in [56, 55, 54]: Kstock[i, j - 54] = ( (Kstock[i, j - 53] * pcelist[j] / pcelist[j + 1] - self.investment_history[i, j]) / (1 - deltalist[i])) trueDep[i, j - 54] = Kstock[i, j - 54] * deltalist[i] # Using 2017 asset totals, apply to future for j in range(57, 68): trueDep[i, j - 54] = Kstock[i, j - 54] * deltalist[i] Kstock[i, j - 53] = ( (Kstock[i, j - 54] + self.investment_history[i, j] - trueDep[i, j - 54]) * pcelist[j + 1] / pcelist[j]) self.capital_history = Kstock self.trueDep = trueDep def build_capital_path(self): """ Builds the DataFrame of asset amount, investment and depreciation totals for each year in the budget window. """ # Sum across assets and put into new dataset Kstock_total = np.zeros(14) fixedK_total = np.zeros(14) trueDep_total = np.zeros(14) inv_total = np.zeros(14) fixedInv_total = np.zeros(14) Mdep_total = np.zeros(14) for j in range(14): adjfactor = self.adjustments['overall'] * self.adjustments[ 'rescalar'][j] Kstock_total[j] = sum(self.capital_history[:, j]) * adjfactor fixedK_total[j] = ( (sum(self.capital_history[:, j]) - self.capital_history[31, j] - self.capital_history[32, j]) * adjfactor) trueDep_total[j] = sum(self.trueDep[:, j]) * adjfactor inv_total[j] = (sum(self.investment_history[:, j + 54]) * adjfactor) fixedInv_total[j] = ((sum(self.investment_history[:, j + 54]) - self.investment_history[31, j + 54]) * adjfactor) Mdep_total[j] = self.calcDep_oneyear(j + 2014) * adjfactor cap_result = pd.DataFrame({ 'year': range(2014, 2028), 'Kstock': Kstock_total, 'Investment': inv_total, 'FixedInv': fixedInv_total, 'trueDep': trueDep_total, 'taxDep': Mdep_total, 'FixedK': fixedK_total }) self.capital_path = cap_result def calc_all(self): """ Executes all calculations for Asset object. """ self.get_ccr_data() self.build_inv_matrix() self.build_deprLaw_matrices() self.build_capital_history() self.build_capital_path() return None def get_forecast(self): """ Returns an array of the capital stock for 2014-2027 """ forecast = np.array(self.capital_path['Kstock']) return forecast def get_taxdep(self): """ Returns an array of tax depreciation deductions for 2014-2027 """ taxdep = np.array(self.capital_path['taxDep']) return taxdep def get_investment(self): """ Returns an array of total investment for 2014-2027 """ inv1 = np.array(self.capital_path['Investment']) return inv1 def get_truedep(self): """ Returns an array of true depreciation for 2014-2027 """ truedep = np.array(self.capital_path['trueDep']) return truedep
def calc_oneyear(self, year): """ In the given year, calculates EATR and user cost of capital for each asset type. """ # Check that year has acceptable value assert year in range(2017, END_YEAR + 1) # Extract economic parameters [r_c, r_nc, r_d, pi, f_c, f_nc] = self.get_econ_params_oneyear(year) # Extract tax depreciation information iyr = year - 1960 Method = self.asset_c.method_history[iyr] Life = self.asset_c.life_history[:, iyr] Bonus = self.asset_c.bonus_history[:, iyr] # Make tax rate dictionaries tdict_c = self.make_tdict_c(year) tdict_nc = self.make_tdict_nc(year) # Create base DataFrame and get depreciation rates asset_data = copy.deepcopy(Data().taxdep_info_gross('pre2017')) asset_data.drop(['L_gds', 'L_ads', 'Method'], axis=1, inplace=True) Delta = np.array(asset_data['delta']) # Get deductible fractions of interest paid (fracded_c, fracded_nc) = self.calc_frac_ded(year) # Get inventory method assets = np.asarray(asset_data['Asset']) uc_c = np.zeros(len(assets)) uc_nc = np.zeros(len(assets)) eatr_c = np.zeros(len(assets)) eatr_nc = np.zeros(len(assets)) for j in range(len(asset_data)): uc_c[j] = self.calc_usercost(r_c, pi, Delta[j], Method[j], Life[j], Bonus[j], f_c, r_d, fracded_c, tdict_c, 50) uc_nc[j] = self.calc_usercost(r_nc, pi, Delta[j], Method[j], Life[j], Bonus[j], f_nc, r_d, fracded_nc, tdict_nc, 50) eatr_c[j] = self.calc_eatr(0.2, r_c, pi, Delta[j], Method[j], Life[j], Bonus[j], f_c, r_d, fracded_c, tdict_c, length=50) eatr_nc[j] = self.calc_eatr(0.2, r_nc, pi, Delta[j], Method[j], Life[j], Bonus[j], f_nc, r_d, fracded_nc, tdict_nc, length=50) # Save the results to the main DataFrame asset_data['uc_c'] = uc_c asset_data['uc_nc'] = uc_nc asset_data['eatr_c'] = eatr_c asset_data['eatr_nc'] = eatr_nc return asset_data
class PassThrough(): """ Constructor for the PassThrough class. This contains the calculation of pass-through business income. All other components of pass-through taxation occur through Tax-Calculator. For now, a PassThrough object contains 6 different business entities: sole proprietorship, positive net income sole proprietorship, negative net income S corporation, positive net income S corporation, negative net income partnership, positive net income partnership, negative net income Therefore, the process for modeling the pass-through sector evolves in two stages. The first, like for the Corporation class, produces a single Asset object, Debt object and earnings for the pass-through sector. Once these are calculated, they are split between each of the 6 entities. The results from these will later be used by the Investor class to distribute the changes in business income to individuals in Tax-Calculator. The following functions apply to the sector as a whole: create_asset() create_earnings() create_debt() real_activity() """ def __init__(self, btax_params): # Store policy parameter objects if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') # Create Data object self.data = Data() # Create Asset object and calculate self.asset = Asset(self.btax_params, corp=False, data=self.data) self.asset.calc_all() # Create earnings forecast self.create_earnings() def create_earnings(self): """ Creates the initial forecast for earnings. Static only. """ # Get initial EBITDA for 2014, for those in net income positions sp_posinc2014 = np.array(self.data.sp_data['netinc'])[-1] part_posinc2014 = np.array(self.data.partner_data['netinc_total'])[-1] scorp_posinc2014 = np.array(self.data.Scorp_data['netinc_total'])[-1] # Get initial EBITDA for 2014, for those in net loss positions sp_neginc2014 = -np.array(self.data.sp_data['netloss'])[-1] part_neginc2014 = -np.array(self.data.partner_data['netloss_total'])[-1] scorp_neginc2014 = -np.array(self.data.Scorp_data['netloss_total'])[-1] # Get growth factor for noncorporate business income and apply it gfact_propinc = np.array(self.data.gfactors['propinc_nonfarm'])[1:] sp_posinc = sp_posinc2014 / gfact_propinc[0] * gfact_propinc part_posinc = part_posinc2014 / gfact_propinc[0] * gfact_propinc scorp_posinc = scorp_posinc2014 / gfact_propinc[0] * gfact_propinc sp_neginc = sp_neginc2014 / gfact_propinc[0] * gfact_propinc part_neginc = part_neginc2014 / gfact_propinc[0] * gfact_propinc scorp_neginc = scorp_neginc2014 / gfact_propinc[0] * gfact_propinc # Aggregate and save EBITDAs total_inc = (sp_posinc + sp_neginc + part_posinc + part_neginc + scorp_posinc + scorp_neginc) earnings_result = pd.DataFrame({'year': range(2014,2028), 'total': total_inc, 'SchC_pos': sp_posinc, 'SchC_neg': sp_neginc, 'partner_pos': part_posinc, 'partner_neg': part_neginc, 'Scorp_pos': scorp_posinc, 'Scorp_neg': scorp_neginc}) self.earnings = earnings_result def create_debt(self): """ Creates the Debt object for the pass-through sector. Note: create_asset must have already been called """ self.debt = Debt(self.btax_params, self.asset.get_forecast(), data=self.data, corp=False) self.debt.calc_all() def real_activity(self): """ Produces a DataFrame of the pass-through sector's real activity. Real measures are: Capital stock Investment Depreciation (economic) Debt Interest paid Earnings Net income Cash flow Note that unlike for a corporation, the final real activity measures (net income and cash flow) are pre-tax, as these would be passed to units in Tax-Calculator. """ real_results = pd.DataFrame({'year': range(2014,2028), 'Earnings': self.earnings['total']}) real_results['Kstock'] = self.asset.get_forecast() real_results['Inv'] = self.asset.get_investment() real_results['Depr'] = self.asset.get_truedep() real_results['Debt'] = self.debt.get_debt() real_results['NIP'] = self.debt.get_nip() real_results['NetInc'] = real_results['Earnings'] - real_results['Depr'] - real_results['NIP'] real_results['CashFlow'] = real_results['Earnings'] - real_results['Inv'] self.real_results = real_results def calc_schC(self): """ Calculates net income for sole proprietorships """ SchC_results = pd.DataFrame({'year': range(2014,2028)}) # Update earnings SchC_results['ebitda_pos'] = self.earnings['SchC_pos'] SchC_results['ebitda_neg'] = self.earnings['SchC_neg'] # Update tax depreciation SchC_results['dep_pos'] = (self.asset.get_taxdep() * self.data.depshare_sp_posinc) SchC_results['dep_neg'] = (self.asset.get_taxdep() * self.data.depshare_sp_neginc) # Update interest deduction SchC_results['intded_pos'] = (self.debt.get_nid() * self.data.intshare_sp_posinc) SchC_results['intded_neg'] = (self.debt.get_nid() * self.data.intshare_sp_neginc) # Update business net income SchC_results['netinc_pos'] = SchC_results['ebitda_pos'] - SchC_results['dep_pos'] - SchC_results['intded_pos'] SchC_results['netinc_neg'] = SchC_results['ebitda_neg'] - SchC_results['dep_neg'] - SchC_results['intded_neg'] self.SchC_results = SchC_results def calc_partner(self): """ Calculates net income for partnerships """ partner_results = pd.DataFrame({'year': range(2014,2028)}) # Update earnings partner_results['ebitda_pos'] = self.earnings['partner_pos'] partner_results['ebitda_neg'] = self.earnings['partner_neg'] # Update tax depreciation partner_results['dep_pos'] = (self.asset.get_taxdep() * self.data.depshare_partner_posinc) partner_results['dep_neg'] = (self.asset.get_taxdep() * self.data.depshare_partner_neginc) # Update interest deduction partner_results['intded_pos'] = (self.debt.get_nid() * self.data.intshare_partner_posinc) partner_results['intded_neg'] = (self.debt.get_nid() * self.data.intshare_partner_neginc) # Update business net income partner_results['netinc_pos'] = partner_results['ebitda_pos'] - partner_results['dep_pos'] - partner_results['intded_pos'] partner_results['netinc_neg'] = partner_results['ebitda_neg'] - partner_results['dep_neg'] - partner_results['intded_neg'] self.partner_results = partner_results def calc_Scorp(self): """ Calculates net income for S corporations """ Scorp_results = pd.DataFrame({'year': range(2014,2028)}) # Update earnings Scorp_results['ebitda_pos'] = self.earnings['Scorp_pos'] Scorp_results['ebitda_neg'] = self.earnings['Scorp_neg'] # Update tax depreciation Scorp_results['dep_pos'] = (self.asset.get_taxdep() * self.data.depshare_scorp_posinc) Scorp_results['dep_neg'] = (self.asset.get_taxdep() * self.data.depshare_scorp_neginc) # Update interest deduction Scorp_results['intded_pos'] = (self.debt.get_nid() * self.data.intshare_scorp_posinc) Scorp_results['intded_neg'] = (self.debt.get_nid() * self.data.intshare_scorp_neginc) # Update business net income Scorp_results['netinc_pos'] = Scorp_results['ebitda_pos'] - Scorp_results['dep_pos'] - Scorp_results['intded_pos'] Scorp_results['netinc_neg'] = Scorp_results['ebitda_neg'] - Scorp_results['dep_neg'] - Scorp_results['intded_neg'] self.Scorp_results = Scorp_results def calc_netinc(self): """ Runs all calculations for each entity and saves the net income results. """ self.calc_schC() self.calc_partner() self.calc_Scorp() netinc_results = pd.DataFrame({'year': range(2014,2028)}) netinc_results['SchC_pos'] = self.SchC_results['netinc_pos'] netinc_results['SchC_neg'] = self.SchC_results['netinc_neg'] netinc_results['partner_pos'] = self.SchC_results['netinc_pos'] netinc_results['partner_neg'] = self.SchC_results['netinc_neg'] netinc_results['Scorp_pos'] = self.SchC_results['netinc_pos'] netinc_results['Scorp_neg'] = self.SchC_results['netinc_neg'] self.netinc_results = netinc_results def calc_static(self): """ Runs the static calculations """ self.create_debt() self.real_activity() self.calc_netinc() def update_legal(self, responses): """ Updates the rescale_corp and rescale_noncorp associated with each Data associated with each object. """ self.data.update_rescaling(responses.rescale_corp, responses.rescale_noncorp) self.asset.data.update_rescaling(responses.rescale_corp, responses.rescale_noncorp) def update_investment(self, responses): """ Updates the Asset object to include investment response. """ # First, save the capital stock by asset type and year (for earnings) self.old_capital_history = copy.deepcopy(self.asset.capital_history) self.asset.update_response(responses.investment_response) self.asset.calc_all() def update_earnings(self, responses): """ Recalculates earnings using the old capital stock by asset type, the new capital stock by asset type (based on the investment response), and the marginal product of capital. """ Kstock_base = copy.deepcopy(self.old_capital_history) Kstock_ref = copy.deepcopy(self.asset.capital_history) deltaK = Kstock_ref - Kstock_base changeEarnings = np.zeros((96, 14)) for j in range(14): # for each year mpk = np.array(responses.investment_response['MPKnc' + str(j + 2014)]) for i in range(96): # by asset changeEarnings[i,j] = deltaK[i,j] * mpk[i] * self.data.adjfactor_dep_noncorp deltaE = np.zeros(14) for j in range(14): deltaE[j] = changeEarnings[:, j].sum() earnings_old = np.array(self.earnings['total']) ebitda_chgfactor = (earnings_old + deltaE) * self.data.rescale_noncorp / earnings_old keylist = list(self.earnings) for key in keylist: self.earnings[key] = self.earnings[key] * ebitda_chgfactor def update_debt(self, responses): """ Replaces the Debt object to use the new asset forecast and Data """ pctch_delta = np.array(responses.debt_response['pchDelta_corp']) self.debt = Debt(self.btax_params, self.asset.get_forecast(), data=self.data, response=pctch_delta, corp=False) self.debt.calc_all() def apply_responses(self, responses): """ Updates Data, Asset, earnings, and Debt to include responses. """ assert isinstance(responses, Response) self.update_legal(responses) self.update_investment(responses) self.update_earnings(responses) self.update_debt(responses) self.real_activity() self.calc_netinc()
def __init__(self, btax_params, revenues, deductions, credit, dmne=None, data=None, assets=None, debts=None): # Create an associated Data object if isinstance(data, Data): self.data = data else: self.data = Data() if isinstance(btax_params, pd.DataFrame): self.btax_params = btax_params else: raise ValueError('btax_params must be DataFrame') if isinstance(revenues, pd.DataFrame): self.revenues = copy.deepcopy(revenues) else: raise ValueError('revenues must be in DataFrame') if isinstance(deductions, pd.DataFrame): self.deductions = copy.deepcopy(deductions) else: raise ValueError('deductions must be in DataFrame') if isinstance(credit, pd.DataFrame): self.credits = copy.deepcopy(credit) else: raise ValueError('credits must be in DataFrame') if dmne is None: # Note: Don't do this in general self.dmne = DomesticMNE(self.btax_params) self.dmne.calc_all() elif isinstance(dmne, DomesticMNE): self.dmne = dmne else: raise ValueError('dmne must be a DomesticMNE object') if assets is not None: if isinstance(assets, Asset): self.assets = assets else: raise ValueError('assets must be Asset object') else: self.assets = Asset(btax_params) self.assets.calc_all() if debts is not None: if isinstance(debts, Debt): self.debts = debts else: raise ValueError('debts must be Debt object') else: assets_forecast = self.assets.get_forecast() self.debts = Debt(btax_params, assets_forecast) self.debts.calc_all() # Prepare unmodeled components of tax return self.revenues['capgains'] = ( self.revenues['capgains'] * (1. - self.btax_params['capgains_corp_hc'])) self.revenues['domestic_divs'] = ( self.revenues['domestic_divs'] * self.btax_params['domestic_dividend_inclusion']) self.revenues['total'] = (self.revenues['receipts'] + self.revenues['rent'] + self.revenues['royalties'] + self.revenues['capgains'] + self.revenues['domestic_divs'] + self.revenues['other'] + self.dmne.dmne_results['foreign_taxinc']) self.deductions['charity'] = (self.deductions['charity'] * (1. - self.btax_params['charity_hc'])) self.deductions['statelocaltax'] = ( self.deductions['statelocaltax'] * (1. - self.btax_params['statelocaltax_hc'])) self.deductions['total'] = ( self.deductions['cogs'] + self.deductions['execcomp'] + self.deductions['wages'] + self.deductions['repairs'] + self.deductions['baddebt'] + self.deductions['rent'] + self.deductions['statelocaltax'] + self.deductions['charity'] + self.deductions['amortization'] + self.deductions['depletion'] + self.deductions['advertising'] + self.deductions['pensions'] + self.deductions['benefits'] + self.deductions['other']) combined = pd.DataFrame({ 'year': range(START_YEAR, END_YEAR + 1), 'ebitda': (self.revenues['total'] - self.deductions['total']) }) # Add tax depreciation combined['taxDep'] = self.assets.get_taxdep() self.combined_return = combined