def test_get_biz_tax(): # Test function for business tax receipts p = Specifications() new_param_values = { 'cit_rate': [0.20], 'delta_tau_annual': [0.06] } p.update_specifications(new_param_values) p.T = 3 w = np.array([1.2, 1.1, 1.2]) Y = np.array([3.0, 7.0, 3.0]) L = np.array([2.0, 3.0, 2.0]) K = np.array([5.0, 6.0, 5.0]) biz_tax = tax.get_biz_tax(w, Y, L, K, p, 'TPI') assert np.allclose(biz_tax, np.array([0.0102, 0.11356, 0.0102]))
def dynamic_revenue_decomposition(base_params, base_tpi, base_ss, reform_params, reform_tpi, reform_ss, num_years=10, include_SS=True, include_overall=True, include_business_tax=True, full_break_out=False, start_year=DEFAULT_START_YEAR, table_format=None, path=None): ''' This function decomposes the source of changes in tax revenues to determine the percentage change in tax revenues that can be attributed to macroeconomic feedback effects. Args: base_params (OG-Core Specifications class): baseline parameters object base_tpi (dictionary): TP output from baseline run base_ss (dictionary): SS output from baseline run reform_params (OG-Core Specifications class): reform parameters object reform_tpi (dictionary): TP output from reform run reform_ss (dictionary): SS output from reform run num_years (integer): number of years to include in table include_SS (bool): whether to include the steady-state results in the table include_overall (bool): whether to include results over the entire budget window as a column in the table include_business_tax (bool): whether to include business tax revenue changes in result full_break_out (bool): whether to break out behavioral and macro effects start_year (integer): year to start table table_format (string): format to return table in: 'csv', 'tex', 'excel', 'json', if None, a DataFrame is returned path (string): path to save table to Returns: table (various): table in DataFrame or string format or `None` if saved to disk .. note:: The decomposition is the following: 1. Simulate the baseline and reform in OG-Core. Save the resulting series of tax revenues. Call these series for the baseline and reform A and D, respectively. 2. Create a third revenue series that is computed using the baseline behavior (i.e., `bmat_s` and `n_mat`) and macro variables (`tr`, `bq`, `r`, `w`), but with the tax function parameter estimates from the reform policy. Call this series B. 3. Create a fourth revenue series that is computed using the reform behavior (i.e., `bmat_s` and `n_mat`) and tax functions estimated on the reform tax policy, but the macro variables (`tr`, `bq`, `r`, `w`) from the baseline. Call this series C. 3. Calculate the percentage difference between B and A -- call this the "static" change from the macro model. Calculate the percentage difference between C and B -- call this the behavioral effects. Calculate the percentage difference between D and C -- call this the macroeconomic effect. The full dynamic effect is difference between C and A. One can apply the percentage difference from the macro feedback effect to ("static") revenue estimates from the policy change to produce an estimate of the revenue including macro feedback. ''' assert isinstance(start_year, (int, np.integer)) assert isinstance(num_years, (int, np.integer)) # Make sure both runs cover same time period assert (base_params.start_year == reform_params.start_year) year_vec = np.arange(start_year, start_year + num_years) start_index = start_year - base_params.start_year year_list = year_vec.tolist() if include_overall: year_list.append(str(year_vec[0]) + '-' + str(year_vec[-1])) if include_SS: year_list.append('SS') table_dict = {'Year': year_list} T, S, J = base_params.T, base_params.S, base_params.J base_etr_params_4D = np.tile( base_params.etr_params[:T, :, :].reshape( T, S, 1, base_params.etr_params.shape[2]), (1, 1, J, 1)) reform_etr_params_4D = np.tile( reform_params.etr_params[:T, :, :].reshape( T, S, 1, reform_params.etr_params.shape[2]), (1, 1, J, 1)) tax_rev_dict = {'indiv': {}, 'biz': {}, 'total': {}} indiv_liab = {} # Baseline IIT + payroll tax liability indiv_liab['A'] = tax.income_tax_liab( base_tpi['r_hh'][:T], base_tpi['w'][:T], base_tpi['bmat_s'], base_tpi['n_mat'][:T, :, :], base_ss['factor_ss'], 0, None, 'TPI', base_params.e, base_etr_params_4D, base_params) # IIT + payroll tax liability using baseline behavior and macros # with the reform tax functions (this is the OG-Core static estimate) indiv_liab['B'] = tax.income_tax_liab( base_tpi['r_hh'][:T], base_tpi['w'][:T], base_tpi['bmat_s'], base_tpi['n_mat'][:T, :, :], base_ss['factor_ss'], 0, None, 'TPI', base_params.e, reform_etr_params_4D, base_params) # IIT + payroll tax liability using reform behavior and baseline # macros indiv_liab['C'] = tax.income_tax_liab( base_tpi['r_hh'][:T], base_tpi['w'][:T], reform_tpi['bmat_s'], reform_tpi['n_mat'][:T, :, :], base_ss['factor_ss'], 0, None, 'TPI', reform_params.e, reform_etr_params_4D, reform_params) # IIT + payroll tax liability from the reform simulation indiv_liab['D'] = tax.income_tax_liab( reform_tpi['r_hh'][:T], reform_tpi['w'][:T], reform_tpi['bmat_s'], reform_tpi['n_mat'][:T, :, :], base_ss['factor_ss'], 0, None, 'TPI', reform_params.e, reform_etr_params_4D, reform_params) # Business tax revenue from the baseline simulation tax_rev_dict['biz']['A'] = tax.get_biz_tax(base_tpi['w'][:T], base_tpi['Y'][:T], base_tpi['L'][:T], base_tpi['K'][:T], base_params, 'TPI') # Business tax revenue found using baseline behavior and macros with # the reform tax rates tax_rev_dict['biz']['B'] = tax.get_biz_tax(base_tpi['w'][:T], base_tpi['Y'][:T], base_tpi['L'][:T], base_tpi['K'][:T], reform_params, 'TPI') # Business tax revenue found using the reform behavior and baseline # macros with the reform tax rates tax_rev_dict['biz']['C'] = tax.get_biz_tax(base_tpi['w'][:T], reform_tpi['Y'][:T], reform_tpi['L'][:T], reform_tpi['K'][:T], reform_params, 'TPI') # Business tax revenue from the reform tax_rev_dict['biz']['D'] = tax.get_biz_tax(reform_tpi['w'][:T], reform_tpi['Y'][:T], reform_tpi['L'][:T], reform_tpi['K'][:T], reform_params, 'TPI') pop_weights = (np.squeeze(base_params.lambdas) * np.tile(np.reshape(base_params.omega[:T, :], (T, S, 1)), (1, 1, J))) for k in indiv_liab.keys(): tax_rev_dict['indiv'][k] = ((indiv_liab[k] * pop_weights).sum(1).sum(1)) tax_rev_dict['total'][k] = (tax_rev_dict['indiv'][k] + tax_rev_dict['biz'][k]) results_for_table = {'indiv': {}, 'biz': {}, 'total': {}} for type in ['indiv', 'biz', 'total']: # Rate change effect pct_change1 = (((tax_rev_dict[type]['B'] - tax_rev_dict[type]['A']) / tax_rev_dict[type]['A']) * 100) # Behavior effect pct_change2 = (((tax_rev_dict[type]['C'] - tax_rev_dict[type]['B']) / tax_rev_dict[type]['B']) * 100) # Macro effect pct_change3 = (((tax_rev_dict[type]['D'] - tax_rev_dict[type]['C']) / tax_rev_dict[type]['C']) * 100) # Dynamic effect (behavior + macro) pct_change4 = (((tax_rev_dict[type]['D'] - tax_rev_dict[type]['B']) / tax_rev_dict[type]['B']) * 100) # Total change in tax revenue (rates + behavior + macro) pct_change5 = (((tax_rev_dict[type]['D'] - tax_rev_dict[type]['A']) / tax_rev_dict[type]['A']) * 100) pct_change_overall1 = (( (tax_rev_dict[type]['B'][start_index:start_index + num_years].sum() - tax_rev_dict[type]['A'] [start_index:start_index + num_years].sum()) / tax_rev_dict[type]['A'][start_index:start_index + num_years].sum()) * 100) pct_change_overall2 = (( (tax_rev_dict[type]['C'][start_index:start_index + num_years].sum() - tax_rev_dict[type]['B'] [start_index:start_index + num_years].sum()) / tax_rev_dict[type]['B'][start_index:start_index + num_years].sum()) * 100) pct_change_overall3 = (( (tax_rev_dict[type]['D'][start_index:start_index + num_years].sum() - tax_rev_dict[type]['C'] [start_index:start_index + num_years].sum()) / tax_rev_dict[type]['C'][start_index:start_index + num_years].sum()) * 100) pct_change_overall4 = (( (tax_rev_dict[type]['D'][start_index:start_index + num_years].sum() - tax_rev_dict[type]['B'] [start_index:start_index + num_years].sum()) / tax_rev_dict[type]['B'][start_index:start_index + num_years].sum()) * 100) pct_change_overall5 = (( (tax_rev_dict[type]['D'][start_index:start_index + num_years].sum() - tax_rev_dict[type]['A'] [start_index:start_index + num_years].sum()) / tax_rev_dict[type]['A'][start_index:start_index + num_years].sum()) * 100) if include_overall: results_for_table[type][1] = np.append( pct_change1[start_index:start_index + num_years], pct_change_overall1) results_for_table[type][2] = np.append( pct_change2[start_index:start_index + num_years], pct_change_overall2) results_for_table[type][3] = np.append( pct_change3[start_index:start_index + num_years], pct_change_overall3) results_for_table[type][4] = np.append( pct_change4[start_index:start_index + num_years], pct_change_overall4) results_for_table[type][5] = np.append( pct_change5[start_index:start_index + num_years], pct_change_overall5) if include_SS: results_for_table[type][1] = np.append(results_for_table[type][1], pct_change1[-1]) results_for_table[type][2] = np.append(results_for_table[type][2], pct_change2[-1]) results_for_table[type][3] = np.append(results_for_table[type][3], pct_change3[-1]) results_for_table[type][4] = np.append(results_for_table[type][4], pct_change4[-1]) results_for_table[type][5] = np.append(results_for_table[type][5], pct_change5[-1]) if full_break_out: if include_business_tax: table_dict = { 'Year': year_list, # IIT and Payroll Taxes 'IIT: Pct Change due to tax rates': results_for_table['indiv'][1], 'IIT: Pct Change due to behavior': results_for_table['indiv'][2], 'IIT: Pct Change due to macro': results_for_table['indiv'][3], 'IIT: Overall Pct Change in taxes': results_for_table['indiv'][5], # Business Taxes 'CIT: Pct Change due to tax rates': results_for_table['biz'][1], 'CIT: Pct Change due to behavior': results_for_table['biz'][2], 'CIT: Pct Change due to macro': results_for_table['biz'][3], 'CIT: Overall Pct Change in taxes': results_for_table['biz'][5], # All Taxes 'All: Pct Change due to tax rates': results_for_table['total'][1], 'All: Pct Change due to behavior': results_for_table['total'][2], 'All: Pct Change due to macro': results_for_table['total'][3], 'All: Overall Pct Change in taxes': results_for_table['total'][5] } else: table_dict = { 'Year': year_list, 'Pct Change due to tax rates': results_for_table['indiv'][1], 'Pct Change due to behavior': results_for_table['indiv'][2], 'Pct Change due to macro': results_for_table['indiv'][3], 'Overall Pct Change in taxes': results_for_table['indiv'][5] } else: if include_business_tax: table_dict = { 'Year': year_list, # 'IIT and Payroll Taxes:': # np.ones(results_for_table['indiv'][1].shape[0]) * np.nan, 'IIT: Pct Change due to tax rates': results_for_table['indiv'][1], 'IIT: Pct Change due to dynamics': results_for_table['indiv'][4], 'IIT: Overall Pct Change in taxes': results_for_table['indiv'][5], # 'Business Taxes:': # np.ones(results_for_table['biz'][1].shape[0]) * np.nan, 'CIT: Pct Change due to tax rates': results_for_table['biz'][1], 'CIT: Pct Change due to dynamics': results_for_table['biz'][4], 'CIT: Overall Pct Change in taxes': results_for_table['biz'][5], # 'All Taxes:': # np.ones(results_for_table['total'][1].shape[0]) * np.nan, 'All: Pct Change due to tax rates': results_for_table['total'][1], 'All: Pct Change due to dynamics': results_for_table['total'][4], 'All: Overall Pct Change in taxes': results_for_table['total'][5] } else: table_dict = { 'Year': year_list, 'Pct Change due to tax rates': results_for_table['indiv'][1], 'Pct Change due to dynamics': results_for_table['indiv'][4], 'Overall Pct Change in taxes': results_for_table['indiv'][5] } # Make df with dict so can use pandas functions table_df = pd.DataFrame.from_dict( table_dict, orient='columns').set_index('Year').transpose() table_df.reset_index(inplace=True) table_df.rename(columns={'index': 'Variable'}, inplace=True) table = save_return_table(table_df, table_format, path) return table
def revenue(r, w, b, n, bq, c, Y, L, K, factor, ubi, theta, etr_params, p, method): r''' Calculate aggregate tax revenue. .. math:: R_{t} = \sum_{s=E}^{E+S}\sum_{j=0}^{J}\omega_{s,t}\lambda_{j} (T_{j,s,t} + \tau^{p}_{t}w_{t}e_{j,s}n_{j,s,t} - \theta_{j} w_{t} + \tau^{bq}bq_{j,s,t} + \tau^{c}_{s,t}c_{j,s,t} + \tau^{w}_{t}b_{j,s,t}) + \tau^{b}_{t}(Y_{t}-w_{t}L_{t}) - \tau^{b}_{t}\delta^{\tau}_{t}K^{\tau}_{t} Args: r (array_like): the real interest rate w (array_like): the real wage rate b (Numpy array): household savings n (Numpy array): household labor supply bq (Numpy array): household bequests received c (Numpy array): household consumption Y (array_like): aggregate output L (array_like): aggregate labor K (array_like): aggregate capital factor (scalar): scaling factor converting model units to dollars ubi (array_like): universal basic income household distributions theta (Numpy array): social security replacement rate for each lifetime income group etr_params (Numpy array): paramters of the effective tax rate functions p (OG-Core Specifications object): model parameters method (str): adjusts calculation dimensions based on 'SS' or 'TPI' Returns: total_tax_revenue (array_like): aggregate tax revenue iit_payroll_tax_revenue (array_like): aggregate income and payroll tax revenue agg_pension_outlays (array_like): aggregate outlays for gov't pensions UBI_outlays (array_like): aggregate universal basic income (UBI) outlays bequest_tax_revenue (array_like): aggregate bequest tax revenue wealth_tax_revenue (array_like): aggregate wealth tax revenue cons_tax_revenue (array_like): aggregate consumption tax revenue business_tax_revenue (array_like): aggregate business tax revenue payroll_tax_revenue (array_like): aggregate payroll tax revenue iit_tax_revenue (array_like): aggregate income tax revenue ''' inc_pay_tax_liab = tax.income_tax_liab(r, w, b, n, factor, 0, None, method, p.e, etr_params, p) pension_benefits = tax.pension_amount(w, n, theta, 0, None, False, method, p.e, p) bq_tax_liab = tax.bequest_tax_liab(r, b, bq, 0, None, method, p) w_tax_liab = tax.wealth_tax_liab(r, b, 0, None, method, p) if method == 'SS': pop_weights = np.transpose(p.omega_SS * p.lambdas) iit_payroll_tax_revenue = (inc_pay_tax_liab * pop_weights).sum() agg_pension_outlays = (pension_benefits * pop_weights).sum() UBI_outlays = (ubi * pop_weights).sum() wealth_tax_revenue = (w_tax_liab * pop_weights).sum() bequest_tax_revenue = (bq_tax_liab * pop_weights).sum() cons_tax_revenue = (p.tau_c[-1, :, :] * c * pop_weights).sum() payroll_tax_revenue = (p.frac_tax_payroll[-1] * iit_payroll_tax_revenue) elif method == 'TPI': pop_weights = (np.squeeze(p.lambdas) * np.tile(np.reshape(p.omega[:p.T, :], (p.T, p.S, 1)), (1, 1, p.J))) iit_payroll_tax_revenue = (inc_pay_tax_liab * pop_weights).sum(1).sum(1) agg_pension_outlays = (pension_benefits * pop_weights).sum(1).sum(1) UBI_outlays = (ubi[:p.T, :, :] * pop_weights).sum(1).sum(1) wealth_tax_revenue = (w_tax_liab * pop_weights).sum(1).sum(1) bequest_tax_revenue = (bq_tax_liab * pop_weights).sum(1).sum(1) cons_tax_revenue = (p.tau_c[:p.T, :, :] * c * pop_weights).sum(1).sum(1) payroll_tax_revenue = (p.frac_tax_payroll[:p.T] * iit_payroll_tax_revenue) business_tax_revenue = tax.get_biz_tax(w, Y, L, K, p, method) iit_revenue = iit_payroll_tax_revenue - payroll_tax_revenue total_tax_revenue = (iit_payroll_tax_revenue + wealth_tax_revenue + bequest_tax_revenue + cons_tax_revenue + business_tax_revenue) return (total_tax_revenue, iit_payroll_tax_revenue, agg_pension_outlays, UBI_outlays, bequest_tax_revenue, wealth_tax_revenue, cons_tax_revenue, business_tax_revenue, payroll_tax_revenue, iit_revenue)