def calcDepAdjustment(corp): """ Calculates the adjustment factor for assets, depreciation and investment corp: indicator for whether corporate or noncorporate data """ # Create Asset object policy = Policy() asset1 = Asset(policy.parameters_dataframe(), corp) asset1.calc_all() # Get unscaled depreciation for all years totalAnnualDepreciation = asset1.calcDep_allyears() # Get IRS data depreciation_data = copy.deepcopy(asset1.data.depreciationIRS_data) depreciation_data['dep_model'] = totalAnnualDepreciation[40:54] if corp: depreciation_data['scale'] = (depreciation_data['dep_Ccorp'] / depreciation_data['dep_model']) else: depreciation_data['scale'] = ( (depreciation_data['dep_Scorp'] + depreciation_data['dep_sp'] + depreciation_data['dep_partner']) / depreciation_data['dep_model']) adj_factor = (sum(depreciation_data['scale']) / len(depreciation_data['scale'])) # depreciation_data.to_csv('depr_data_' + str(corp) + '.csv') return adj_factor
def calcIDAdjustment(corp, eta=0.4): """ Calculates the adjustment factors for the corporate and noncorporate debt and interest. eta: retirement rate of existing debt """ 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 net interest deduction NID_gross = debt.NID[38:54] # Get net interest deduction from historical IRS data if corp: NID_irs = np.array(data1.debt_data_corp['NID_IRS'])[38:54] else: NID_irs = np.array(data1.debt_data_noncorp['ID_Scorp'][38:54] + data1.debt_data_noncorp['ID_sp'][38:54] + data1.debt_data_noncorp['ID_partner'][38:54]) NID_scale = sum(NID_irs / NID_gross) / 16.0 # 16 = 54 - 38 return NID_scale
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 __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 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 BtaxMini(): """ Constructor for the BtaxMini class. This class functions similarly to the B-Tax model, with several modifications, such as the use of different equations and allowing for nonconstant tax rates, as well as producing somewhat different final measures. Parameters ---------- btax_params: DataFrame of regular tax parameters Returns ------- DataFrame of user cost of capital and EATR for each year and asset type """ 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() def make_tdict_c(self, start_year): """ Produces a dictionary of tax rates and changes to those rates, for use when calculating rho and EATR, for corporations. Assumes no changes after 2027. """ assert start_year >= 2017 assert type(start_year) == int if start_year >= 2027: tdict = {'0': self.btax_params['tau_c'][10]} else: tdict = {'0': self.btax_params['tau_c'][start_year - 2014]} for i in range(start_year - 2016, len(self.btax_params['year']) - 3): if self.btax_params['tau_c'][ i + 3] != self.btax_params['tau_c'][i + 2]: tdict[str(i - (start_year - 2017))] = self.btax_params['tau_c'][i + 3] return tdict def make_tdict_nc(self, start_year): """ Produces a dictionary of tax rates and changes to those rates, for use when calculating rho and EATR, for noncorporate businesses. Assumes no changes after 2027. """ assert start_year >= 2017 assert type(start_year) == int if start_year >= 2027: tdict = {'0': self.btax_params['tau_nc'][13]} else: tdict = {'0': self.btax_params['tau_nc'][start_year - 2014]} for i in range(start_year - 2016, len(self.btax_params['year']) - 3): tdict[str(i - (start_year - 2017))] = self.btax_params['tau_nc'][i + 3] return tdict def get_econ_params_oneyear(self, year): """ Extracts the economic parameters to use for calculations in each year. """ r_d = self.econ_params['r_d'][year - 2014] r_e_c = self.econ_params['r_e_c'][year - 2014] r_e_nc = self.econ_params['r_e_nc'][year - 2014] pi = self.econ_params['pi'][year - 2014] f_c = self.econ_params['f_c'][year - 2014] f_nc = self.econ_params['f_nc'][year - 2014] r_c = f_c * r_d + (1 - f_c) * r_e_c r_nc = f_nc * r_d + (1 - f_nc) * r_e_nc return (r_c, r_nc, r_d, pi, f_c, f_nc) def calc_frac_ded(self, year): """ Calculates the fraction of interest deductible for the given year, for corporate and noncorporate. """ # Extract the corporate interest haircuts hc_nid_c = np.array(self.btax_params['netIntPaid_corp_hc'])[year - 2014] hc_id_new_year_c = np.array( self.btax_params['newIntPaid_corp_hcyear'])[year - 2014] hc_id_new_c = np.array(self.btax_params['newIntPaid_corp_hc'])[year - 2014] # Find haircut for corporations fracdedc = 1.0 - hc_nid_c if year >= hc_id_new_year_c: fracdedc = min(fracdedc, 1.0 - hc_id_new_c) hc_id_new_year_nc = np.array( self.btax_params['newIntPaid_noncorp_hcyear'])[year - 2014] hc_id_new_nc = np.array( self.btax_params['newIntPaid_noncorp_hc'])[year - 2014] # Find haircut for noncorporate businesses if year < hc_id_new_year_nc: # If not subject to haircut fracdedn = 1.0 else: # If subject to haircut fracdedn = 1.0 - hc_id_new_nc return (fracdedc, fracdedn) def calc_I(self, delta, r, a, b): """ Calculates present value of income occuring during the period [a,b] delta: depreciation rate r: discount rate Note: this is based on unit incone amount """ if r + delta == 0: I = b - a else: I = (1 / (r + delta) * np.exp(-(r + delta) * a) * (1 - np.exp(-(r + delta) * (b - a)))) return I def calc_Ilist(self, delta, r, length=50): """ Calculates present value of income unit over lifetime delta: depreciation rate r: discount rate length: number of periods to use """ # Calculate for first (half) year I0 = self.calc_I(delta, r, 0, 0.5) Ilist = [I0] for j in range(1, length - 1): Ilist.append(self.calc_I(delta, r, j - 0.5, j + 0.5)) # Calculate from final period to infinity Ilist.append(self.calc_I(delta, r, length - 1 - 0.5, 9e99)) return Ilist def calc_F(self, f, r, i, delta, fracded, a, b): """ Calculates present value of interest deduction during period [a,b] f: ratio of debt to assets r: discount rate i: interest rate on debt delta: depreciation rate fracded: fraction of interest paid deductible """ F = (f * i / (r + delta) * fracded * np.exp(-(r + delta) * a) * (1 - np.exp(-(r + delta) * (b - a)))) return F def calc_Flist(self, f, r, i, delta, fracded, length=50): """ Calculates present value of interest deduction over lifetime f: ratio of debt to assets r: discount rate i: interest rate on debt delta: depreciation rate fracded: fraction of interest paid deductible length: number of periods to use """ # Calcuate for first (half) year Flist = [self.calc_F(f, r, i, delta, fracded, 0, 0.5)] for j in range(1, length - 1): Flist.append(self.calc_F(f, r, i, delta, fracded, j - 0.5, j + 0.5)) # Calculate from final period to infinity Flist.append( self.calc_F(f, r, i, delta, fracded, length - 1 - 0.5, 9e99)) return Flist def calc_Dlist_exp(self, length=50): """ Calculates depreciation deduction vector for expensing Note: by default, this is 1 for the first period and 0 thereafter """ Dlist = np.zeros(length) Dlist[0] = 1 return Dlist def calc_D_econ(self, delta, r, a, b): """ Calculates PV of depreciation deduction during [a,b] using economic depreciation method. delta: depreciation rate r: discount rate """ if r + delta == 0: D = delta * (b - a) else: D = (delta / (r + delta) * np.exp(-(r + delta) * a) * (1 - np.exp(-(r + delta) * (b - a)))) return D def calc_Dlist_econ(self, delta, r, bonus, length=50): """ Calculates present value of depreciation deductions over lifetime for economic depreciation. delta: depreciation rate r: discount rate bonus: bonus depreciation rate """ # Calculate for fist (half) year Dlist = [bonus + (1 - bonus) * self.calc_D_econ(delta, r, 0, 0.5)] for j in range(1, length - 1): Dlist.append( (1 - bonus) * self.calc_D_econ(delta, r, j - 0.5, j + 0.5)) # Calculate from last period to infinity Dlist.append( (1 - bonus) * self.calc_D_econ(delta, r, length - 1 - 0.5, 9e99)) return Dlist def calc_D_dbsl(self, N, L, r, pi, a, b): """ Calculates PV of depreciation deductions during [a,b] for declining balance and straight-line depreciation. N: exponential depreciation: 2 for double-declining balance 1.5 for 150% declining balance 1 for straight-line L: tax life r: discount rate pi: inflation rate """ # Ensure N is not an int N = N * 1.0 # Switching point t1 = L * (1 - 1 / N) # End of tax life t2 = L if b <= t1: # If entirely subject to exponential depreciation D = (N / L / (r + pi + N / L) * np.exp(-(r + pi + N / L) * a) * (1 - np.exp(-(r + pi + N / L) * (b - a)))) elif b <= t2: if a < t1: # If period splits exponential and straight-line depreciation Ddb = (N / L / (r + pi + N / L) * np.exp(-(r + pi + N / L) * a) * (1 - np.exp(-(r + pi + N / L) * (t1 - a)))) if r + pi == 0: # Special case of zero nominal discount rate Dsl = np.exp(1 - N) * (b - t1) / (t2 - t1) else: Dsl = (N / L / (r + pi) * np.exp(1 - N) * np.exp(-(r + pi) * t1) * (1 - np.exp(-(r + pi) * (b - t1)))) D = Ddb + Dsl else: # If entirely subject to straight-line depreciation if r + pi == 0: D = np.exp(1 - N) * (b - a) / (t2 - t1) else: D = (N / L / (r + pi) * np.exp(1 - N) * np.exp(-(r + pi) * a) * (1 - np.exp(-(r + pi) * (b - a)))) else: # end of period occurs after tax life ends if a < t2: # If tax life ends during period if r + pi == 0: D = np.exp(1 - N) * (t2 - a) / (t2 - t1) else: D = (N / L / (r + pi) * np.exp(1 - N) * np.exp(-(r + pi) * a) * (1 - np.exp(-(r + pi) * (t2 - a)))) else: # If period occurs entirely after tax life has ended D = 0 return D def calc_Dlist_dbsl(self, N, L, bonus, r, pi, length=50): """ Calculates present value of depreciation deductions over lifetime for declining balance and straight-line depreciation. N: exponential depreciation: 2 for double-declining balance 1.5 for 150% declining balance 1 for straight-line L: tax life r: discount rate pi: inflation rate length: number of periods to use """ Dlist = [bonus + (1 - bonus) * self.calc_D_dbsl(N, L, r, pi, 0, 0.5)] for j in range(1, length): Dlist.append( (1 - bonus) * self.calc_D_dbsl(N, L, r, pi, j - 0.5, j + 0.5)) return Dlist def calc_Dlist(self, method, life, delta, r, pi, bonus, length=50): """ Calculates present value of depreciation deductions over lifetime. method: depreciation method to use life: tax life delta: depreciation rate r: discount rate pi: inflation rate bonus: bonus depreciation rate length: number of periods to use """ # Check that methods are acceptable assert method in [ 'DB 200%', 'DB 150%', 'SL', 'Economic', 'Expensing', 'None' ] # Check bonus depreciation rates assert bonus >= 0 and bonus <= 1 if type(length) != int: length = int(length) if method == 'DB 200%': # Double-declining (200%) balance depreciation Dlist = self.calc_Dlist_dbsl(2, life, bonus, r, pi, length) elif method == 'DB 150%': # 150% declining balance depreciation Dlist = self.calc_Dlist_dbsl(1.5, life, bonus, r, pi, length) elif method == 'SL': # Straight-line depreciation Dlist = self.calc_Dlist_dbsl(1.0, life, bonus, r, pi, length) elif method == 'Economic': # Economic depreciation Dlist = self.calc_Dlist_econ(delta, r, bonus, length) elif method == 'Expensing': # Expensing Dlist = self.calc_Dlist_exp(length) else: # No depreciation Dlist = np.zeros(length) return Dlist def calc_Tlist(self, tdict, length=50): """ Builds list of statutory tax rates for each period in lifetime tdict: dictionary of tax rates and when they become effective tdict may not be empty tdict must contain at least one key of '0' tdict keys must be as nonnegative integers length: number of periods to use """ assert len(tdict) > 0 # changelist is the period when the tax rate changes changelist = [] for key in tdict: changelist.append(int(key)) changelist.sort() # ratelist is list of tax rates ratelist = [] for chg in changelist: ratelist.append(tdict[str(chg)]) numrates = len(ratelist) rateind = 0 # Get initial tax rate Tlist = [tdict[str(changelist[0])]] for j in range(1, length): if rateind + 1 == numrates: # If at end of tax rate changes Tlist.append(ratelist[rateind]) else: if j < changelist[rateind + 1]: # If between tax rate changes Tlist.append(ratelist[rateind]) else: # If tax rate change occurs this year rateind = rateind + 1 Tlist.append(ratelist[rateind]) return Tlist def calc_rho(self, r, pi, delta, method, life, bonus, f, rd, fracded, tdict, length=50): """ Calculates the cost of capital r: discount rate pi: inflation rate delta: depreciation rate method: depreciation method life: tax life bonus: bonus depreciation rate f: debt to asset ratio rd: interest rate on debt fracded: fraction of interest paid deductible tdict: dict of tax rates and changes length: number of periods to use """ # Get tax rates for all periods Tlist = np.asarray(self.calc_Tlist(tdict, length)) # Get income rates for all periods Nlist = np.asarray(self.calc_Ilist(delta, r, length)) # Get depreciation deductions for all periods Dlist = np.asarray( self.calc_Dlist(method, life, delta, r, pi, bonus, length)) # Get interest deductions for all periods Flist = np.asarray(self.calc_Flist(f, r, rd, delta, fracded, length)) # Present value of tax shield from depreciation A = sum(Dlist * Tlist) # Present value of tax shield from interest deduction F = sum(Flist * Tlist) # Present value of gross income net-of-tax rate N = sum(Nlist * (1 - Tlist)) rho = (1 - A - F) / N - delta return rho def calc_rho_inv(self, r, pi, inv_method, hold, tdict): """ Calculates the cost of capital for inventories r: discount rate pi: inflation rate inv_method: inventory accounting method hold: holding period for inventories tdict: dict of tax rates and changes """ # Acceptable inventory methods assert inv_method in ['FIFO', 'LIFO', 'Expensing', 'Mix'] tau = tdict['0'] # Cost of capital with expensing rho_exp = r # Cost of capital using LIFO rho_lifo = (1 / hold * np.log((np.exp( (r + pi) * hold) - tau) / (1 - tau)) - pi) # Cost of capital using FIFO rho_fifo = 1 / hold * np.log((np.exp(r * hold) - tau) / (1 - tau)) if inv_method == 'FIFO': rho_inv = rho_fifo elif inv_method == 'LIFO': rho_inv = rho_lifo elif inv_method == 'Expensing': rho_inv = rho_exp else: # Mix of 50% FIFO and 50% LIFO rho_inv = 0.5 * (rho_fifo + rho_lifo) return rho_inv def calc_eatr(self, p, r, pi, delta, method, life, bonus, f, rd, fracded, tdict, length=50): """ Calculates the effective average tax rate on investment p: financial income rate r: discount rate pi: inflation rate delta: depreciation rate method: depreciation method life: tax life bonus: bonus depreciation rate f: debt to asset ratio rd: interest rate on debt fracded: fraction of interest paid deductible tdict: dict of tax rates and changes length: number of periods to use """ # Calculate the cost of capital coc = self.calc_rho(r, pi, delta, method, life, bonus, f, rd, fracded, tdict, length) # Check that the financial income rate exceess the cost of capital assert p >= coc # Rent in the absence of tax Rstar = (p - r) / (r + delta) # Income stream P = p / (r + delta) Tlist = np.asarray(self.calc_Tlist(tdict, length)) Nlist = np.asarray(self.calc_Ilist(delta, r, length)) Dlist = np.asarray( self.calc_Dlist(method, life, delta, r, pi, bonus, length)) Flist = np.asarray(self.calc_Flist(f, r, rd, delta, fracded, length)) A = sum(Dlist * Tlist) F = sum(Flist * Tlist) N = sum(Nlist * (1 - Tlist)) # Calculate after-tax rent R = -(1 - A - F) + (p + delta) * N eatr = (Rstar - R) / P return eatr def calc_usercost(self, r, pi, delta, method, life, bonus, f, rd, fracded, tdict, length=50): """ Calculates the cost of capital r: discount rate pi: inflation rate delta: depreciation rate method: depreciation method life: tax life bonus: bonus depreciation rate f: debt to asset ratio rd: interest rate on debt fracded: fraction of interest paid deductible tdict: dict of tax rates and changes length: number of periods to use """ # Calculate the cost of capital coc = self.calc_rho(r, pi, delta, method, life, bonus, f, rd, fracded, tdict, length) ucoc = coc + delta return ucoc 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 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 __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 CorpTaxReturn(): """ Constructor for the CorpTaxReturn object. This class includes objects relevant to the calculation of corporate income tax liability: assets: an associated Asset object debts: an associated debt object combined_return: a DataFrame with tax calculations for each year Parameters: btax_params: dict of business tax policy parameters assets: Asset object for the corporation debts: Debt object for the corporation earnings: list or array of earnings for each year in the budget window """ 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 update_assets(self, assets): """ Updates the Asset object associated with the tax return. """ if isinstance(assets, Asset): self.assets = assets else: raise ValueError('assets must be Asset object') def update_debts(self, debts): """ Updates the Debt object associated with the tax return. """ if isinstance(debts, Debt): self.debts = debts else: raise ValueError('debts must be Debt object') def update_earnings(self, earnings): """ Updates the earnings DataFrame associated with the tax return. """ assert len(earnings) == 14 self.combined_return['ebitda'] = earnings def calcSec199(self): """ Calculates section 199 deduction. """ # Extract relevant parmeters s199_hclist = np.array(self.btax_params['sec199_hc']) profit = np.asarray(self.data.gfactors['profit_d']) sec199_res = np.zeros(14) sec199_2013 = np.asarray(self.data.historical_taxdata['sec199'])[-1] for i in range(14): sec199_res[i] = profit[i+1] / profit[0] * sec199_2013 * (1 - s199_hclist[i]) self.combined_return['sec199'] = sec199_res def calcInitialTax(self): """ Calculates taxable income and tax before credits. """ self.combined_return['taxinc'] = (self.combined_return['ebitda'] - self.combined_return['taxDep'] - self.combined_return['nid'] - self.combined_return['sec199']) self.combined_return['tau'] = self.btax_params['tau_c'] self.combined_return['taxbc'] = (self.combined_return['taxinc'] * self.combined_return['tau']) def calcFTC(self): """ Calculates foreign tax credit for 2014-2027. """ hclist = np.array(self.btax_params['ftc_hc']) def calcWAvgTaxRate(year): """ Calculates the weighted average statutory corporate tax rate in all OECD countries in a given year. """ assert year in range(1995, 2028) year = min(year, 2016) gdp_list = np.asarray(self.data.ftc_gdp_data[str(year)]) taxrate_list = np.asarray(self.data.ftc_taxrates_data[str(year)]) # remove observations with missing data taxrate_list2 = np.where(np.isnan(taxrate_list), 0, taxrate_list) gdp_list2 = np.where(np.isnan(taxrate_list), 0, gdp_list) avgrate = sum(taxrate_list2 * gdp_list2) / sum(gdp_list2) return avgrate # Get foreign profits forecast profits = np.asarray(self.data.ftc_other_data['C_total'][19:]) profits_d = np.asarray(self.data.ftc_other_data['C_domestic'][19:]) tax_f = np.zeros(14) for i in range(14): tax_f[i] = calcWAvgTaxRate(i + 2014) ftc_final = ((profits - profits_d) * tax_f / 100. * self.data.adjfactor_ftc_corp * (1 - hclist)) * self.data.rescale_corp self.combined_return['ftc'] = ftc_final def calcAMT(self): """ Calculates the AMT revenue and PYMTC for 2014-2027 pymtc_status: 0 for no change, 1 for repeal, 2 for refundable """ # Get relevant tax information taxinc = np.array(self.combined_return['taxinc']) amt_rates = np.array(self.btax_params['tau_amt']) ctax_rates = np.array(self.btax_params['tau_c']) pymtc_status = np.array(self.btax_params['pymtc_status']) # Check values for PYMTC status for x in pymtc_status: assert x in [0, 1, 2] # Create empty arrays for AMT, PYMTC, and stocks (by status) A = np.zeros(14) P = np.zeros(14) stockA = np.zeros(15) stockN = np.zeros(15) stockA[0] = ((self.data.trans_amt1 * self.data.userate_pymtc + self.data.trans_amt2 * (1 - self.data.userate_pymtc)) / (1 - self.data.trans_amt1) * self.data.stock2014) stockN[0] = self.data.stock2014 - stockN[0] stockN[0] = (1 - self.data.trans_amt1) / (1 - self.data.trans_amt1 + self.data.trans_amt1 * self.data.userate_pymtc + self.data.trans_amt2 * (1 - self.data.userate_pymtc)) * self.data.stock2014 stockA[0] = self.data.stock2014 - stockN[0] for i in range(14): # Calculate AMT if amt_rates[i] == 0.: # If no AMT A[i] = 0. frac_amt = 0. elif ctax_rates[i] <= amt_rates[i]: # If AMT rate exceeds regular rate (all subject to AMT) A[i] = ((amt_rates[i] - ctax_rates[i] + amt_rates[i] / self.data.param_amt) * taxinc[i]) frac_amt = 0.999 else: A[i] = (amt_rates[i] / self.data.param_amt * np.exp(-self.data.param_amt * (ctax_rates[i] / amt_rates[i] - 1)) * taxinc[i]) frac_amt = np.exp(-self.data.param_amt * (ctax_rates[i] / amt_rates[i] - 1)) # Adjust transition params for change in AMT frequency alpha = max(0.0, min(1.0, self.data.trans_amt1 * (frac_amt / self.data.amt_frac)**0.5)) beta = (1 - alpha) * frac_amt / (1 - frac_amt) if pymtc_status[i] == 0: # No change from baseline userate = self.data.userate_pymtc elif pymtc_status[i] == 1: # PYMTC repealed userate = 0.0 else: # PYMTC made fully refundable userate = 1.0 P[i] = userate * stockN[i] stockA[i+1] = (alpha * (stockA[i] + A[i]) + beta * (stockN[i] - P[i])) stockN[i+1] = ((1 - alpha) * (stockA[i] + A[i]) + (1 - beta) * (stockN[i] - P[i])) # Rescale for any cross-sector shifting amt_final = A * self.data.rescale_corp pymtc_final = P * self.data.rescale_corp self.combined_return['amt'] = amt_final self.combined_return['pymtc'] = pymtc_final def calcTax(self): """ Calculates final tax liability. """ # Calculate general business credits profit = np.asarray(self.data.gfactors['profit_d']) gbc_2013 = np.asarray(self.data.historical_taxdata['gbc'])[-1] gbc_res = profit[1:] / profit[0] * gbc_2013 self.combined_return['gbc'] = gbc_res # Calculate final tax liability self.combined_return['taxrev'] = (self.combined_return['taxbc'] + self.combined_return['amt'] - self.combined_return['ftc'] - self.combined_return['pymtc'] - self.combined_return['gbc']) def calc_all(self): """ Executes all tax calculations. """ self.calcSec199() self.calcInitialTax() self.calcFTC() self.calcAMT() self.calcTax() def getReturn(self): """ Returns the tax return information """ combined_result = copy.deepcopy(self.combined_return) return combined_result def get_tax(self): """ Returns the total tax liability. """ tax1 = np.array(self.combined_return['taxrev']) return tax1
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
class CorpTaxReturn(): """ Constructor for the CorpTaxReturn object. This class includes objects relevant to the calculation of corporate income tax liability: assets: an associated Asset object debts: an associated debt object combined_return: a DataFrame with tax calculations for each year Parameters: btax_params: dict of business tax policy parameters assets: Asset object for the corporation debts: Debt object for the corporation earnings: list or array of earnings for each year in the budget window """ 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 def update_assets(self, assets): """ Updates the Asset object associated with the tax return. """ if isinstance(assets, Asset): self.assets = assets else: raise ValueError('assets must be Asset object') def update_debts(self, debts): """ Updates the Debt object associated with the tax return. """ if isinstance(debts, Debt): self.debts = debts else: raise ValueError('debts must be Debt object') def update_earnings(self, dearnings): """ Updates the earnings DataFrame associated with the tax return. """ assert len(dearnings) == NUM_YEARS fearnings = np.asarray(self.dmne.dmne_results['foreign_taxinc']) self.combined_return['ebitda'] = dearnings + fearnings def calcInterestDeduction(self): """ Computes interest deduction. """ # Compute adjusted taxable income adjTaxInc = np.maximum( self.combined_return['ebitda'] - self.revenues['capgains'] - self.combined_return['taxDep'] + self.btax_params['adjustedTaxInc_def'] * (self.combined_return['taxDep'] + self.deductions['amortization'] + self.deductions['depletion']), 0.0001) # Section 163(j) deduction limitation deductible_int = ( adjTaxInc * self.btax_params['adjustedTaxInc_limit'] + self.debts.get_intInc()) intded0 = self.debts.get_intDed() intded1 = np.zeros(NUM_YEARS) for i in range(NUM_YEARS): if i > 0: # Add disallowed interest as carryforward from prior year intded0[i] = intded0[i] + intded0[i - 1] - intded1[i - 1] intded1[i] = min(deductible_int[i], intded0[i]) # Apply interest haircuts intTaxInc = (self.debts.get_intInc() * (1. - self.btax_params['intIncome_corp_hc']) + self.debts.get_muniInc() * (1. - self.btax_params['muniIntIncome_corp_hc'])) intTaxDed = intded1 * (1. - self.btax_params['intPaid_corp_hc']) # Compute net interest deduction self.combined_return['nid'] = intTaxDed - intTaxInc # Assign fraction of interest deductible to Debt object fracded = intTaxDed / (self.debts.get_intPaid() + 0.000001) self.btax_params['fracded_c'] = fracded def calcInitialTax(self): """ Calculates taxable income and tax before credits. """ netinc1 = (self.combined_return['ebitda'] - self.combined_return['taxDep'] - self.combined_return['nid']) self.combined_return['sec199'] = (netinc1 * self.deductions['sec199share'] * self.btax_params['sec199_rt']) netinc2 = netinc1 - self.combined_return['sec199'] self.combined_return['taxinc'] = np.maximum(netinc2, 0.) self.combined_return['tau'] = self.btax_params['tau_c'] self.combined_return['taxbc'] = (self.combined_return['taxinc'] * self.combined_return['tau']) def calcFTC(self): """ Gets foreign tax credit from DomesticMNE """ self.combined_return['ftc'] = self.dmne.dmne_results['ftc'] def calcAMT(self): """ Calculates the AMT revenue and PYMTC for [START_YEAR, END_YEAR] """ # Overall transition rates and parameters trans_amt0 = self.data.trans_amt0 trans_amt1 = self.data.trans_amt1 userate = self.data.userate_pymtc amt2013 = self.data.corp_tax2013.loc[40, 'ALL'] # Get relevant tax information taxinc = np.array(self.combined_return['taxinc']) amt_rates = np.array(self.btax_params['tau_amt']) ctax_rates = np.array(self.btax_params['tau_c']) pymtc_hc = np.array(self.btax_params['pymtc_hc']) pymtc_refund = np.array(self.btax_params['pymtc_refund']) # Create empty arrays for AMT, PYMTC, and stocks (by status) A = np.zeros(NUM_YEARS) P = np.zeros(NUM_YEARS) stock0 = np.zeros(NUM_YEARS + 1) stock1 = np.zeros(NUM_YEARS + 1) # Set initial stocks using steady-state equations stock0[0] = amt2013 / userate stock1[0] = amt2013 * (trans_amt1 / (1. - trans_amt1) + (1. - userate) / userate * (1. - trans_amt0) / (1. - trans_amt1)) for iyr in range(NUM_YEARS): # Calculate AMT if amt_rates[iyr] == 0.: # If no AMT A[iyr] = 0. frac_amt = 0. # Update transition rate parameters pi0 = 1. pi1 = 0. elif ctax_rates[iyr] <= amt_rates[iyr]: # If AMT rate exceeds regular rate (all subject to AMT) A[iyr] = ((amt_rates[iyr] - ctax_rates[iyr] + amt_rates[iyr] / self.data.param_amt) * taxinc[iyr]) frac_amt = 0.999 # Update transition rate parameters pi0 = 0. pi1 = 1. else: A[iyr] = (amt_rates[iyr] / self.data.param_amt * np.exp(-self.data.param_amt * (ctax_rates[iyr] / amt_rates[iyr] - 1)) * taxinc[iyr]) # Compute new fraction subject to AMT frac_amt = np.exp(-self.data.param_amt * (ctax_rates[iyr] / amt_rates[iyr] - 1)) # Adjust transition params for change in AMT frequency pi1 = max( min( self.data.trans_amt1 * (frac_amt / self.data.amt_frac)**0.5, 1.), 0.) pi0 = max(min(1. - frac_amt * (1 - pi1) / (1 - frac_amt), 1.), 0.) # Compute PYMTC P[iyr] = ((pymtc_refund[iyr] * stock0[iyr] + (1. - pymtc_refund[iyr]) * stock0[iyr] * userate) * (1. - pymtc_hc[iyr])) # Update credits carried forward stock0[iyr + 1] = ((stock1[iyr] + A[iyr]) * (1. - pi1) + (stock0[iyr] - P[iyr]) * pi0) stock1[iyr + 1] = ((stock1[iyr] + A[iyr]) * pi1 + (stock0[iyr] - P[iyr]) * (1. - pi0)) # Rescale for any cross-sector shifting amt_final = A * self.data.rescale_corp pymtc_final = P * self.data.rescale_corp self.combined_return['amt'] = amt_final self.combined_return['pymtc'] = pymtc_final def calcTax(self): """ Calculates final tax liability. """ self.combined_return['gbc'] = self.credits['gbc'] # Calculate final tax liability taxliab1 = (self.combined_return['taxbc'] + self.combined_return['amt'] - self.combined_return['ftc'] - self.combined_return['pymtc'] - self.combined_return['gbc']) self.combined_return['taxrev'] = np.maximum(taxliab1, 0.) def calc_all(self): """ Executes all tax calculations. """ self.calcInterestDeduction() self.calcInitialTax() self.calcFTC() self.calcAMT() self.calcTax() def getReturn(self): """ Returns the tax return information """ combined_result = copy.deepcopy(self.combined_return) return combined_result def get_tax(self): """ Returns the total tax liability. """ tax = np.array(self.combined_return['taxrev']) return tax
assets14 = forecast[0] return (scales, assets14, intpaid_model, intinc_model, muniinc_model) (scales_corp, ast_c_14, intpaid_model, intinc_model, muniinc_model) = calcIDAdjustment(True) (scale_ncorp, ast_nc_14, _, _, _) = calcIDAdjustment(False) print(scales_corp) print(scale_ncorp) newdebt = copy.deepcopy(debt2) newdebt['L_c'] = debt2['L_c'] * scales_corp[0] newdebt['L_nc'] = debt2['L_nc'] * scale_ncorp newdebt['At_c'] = debt2['At_c'] * scales_corp[1] newdebt['An_c'] = debt2['An_c'] * scales_corp[2] newdebt.to_csv(OUTPUT_PATH + 'debt_history.csv') pol1 = Policy() # Create Asset object asset = Asset(pol1.parameters_dataframe(), True) asset.calc_all() # Create Debt object debt = Debt(pol1.parameters_dataframe(), asset.get_forecast(), corp=True) debt.calc_all() df1 = pd.DataFrame({'inc_mod': intinc_model * scales_corp[1], 'paid_mod': intpaid_model * scales_corp[0], 'muni_mod': muniinc_model * scales_corp[2], 'inc_irs': taxint, 'paid_irs': intpaid, 'muni_irs': ntaxint}) df1.to_csv('debt_fit.csv')
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()
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