diff --git a/docs/index.html b/docs/index.html index f0ed26cb2..21c90fc3b 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1024,7 +1024,7 @@

Consumption Parameters

Behavior Parameters

-

Behavior — Taxable Income
tc Name: _BE_inc
TB Name: Income elasticity of taxable income
Description: Defined as dollar change in taxable income divided by dollar change in after-tax income caused by the reform. Must be zero or negative.
Default Value:
2013: 0.0

Behavior — Taxable Income
tc Name: _BE_sub
TB Name: Substitution elasticity of taxable income
Description: Defined as proportional change in taxable income divided by proportional change in marginal net-of-tax rate (1-MTR) on taxpayer earnings caused by the reform. Must be zero or positive.
Default Value:
2013: 0.0

Behavior — Capital Gains
tc Name: _BE_cg
TB Name: Semi-elasticity of long-term capital gains
Description: Defined as change in logarithm of long-term capital gains divided by change in marginal tax rate (MTR) on long-term capital gains caused by the reform. Must be zero or negative. Read Behavior.response documentation for discussion of appropriate values.
Default Value:
2013: 0.0

Response Parameter — Behavior
tc Name: _BE_charity
Long Name: Elasticity of charitable contributions
Description: Defined as proportional change in cash and non-cash charitable contributions divided by proportional change in the after-tax price of charitable contributions caused by the reform. Must be zero or negative.
Default Value:
   for: [AGI < 50k, 50k <= AGI < 100k, 100k <= AGI]
2013: [0.0, 0.0, 0.0]

+

Behavior — Taxable Income
tc Name: _BE_inc
TB Name: Income elasticity of taxable income
Description: Defined as dollar change in taxable income divided by dollar change in after-tax income caused by the reform. Must be zero or negative.
Default Value:
2013: 0.0

Behavior — Taxable Income
tc Name: _BE_sub
TB Name: Substitution elasticity of taxable income
Description: Defined as proportional change in taxable income divided by proportional change in marginal net-of-tax rate (1-MTR) on taxpayer earnings caused by the reform. Must be zero or positive.
Default Value:
2013: 0.0

Behavior — Taxable Income
tc Name: _BE_subinc_wrt_earnings
TB Name: Income and substitution elasticities apply to earnings
Description: False implies elasticities apply to taxable income; true implies they are both computed as proportional change in marginal net-of-tax rate (1-MTR) on taxpayer earnings caused by the reform and apply to taxpayer earnings.
Default Value:
2013: False

Behavior — Capital Gains
tc Name: _BE_cg
TB Name: Semi-elasticity of long-term capital gains
Description: Defined as change in logarithm of long-term capital gains divided by change in marginal tax rate (MTR) on long-term capital gains caused by the reform. Must be zero or negative. Read Behavior.response documentation for discussion of appropriate values.
Default Value:
2013: 0.0

Response Parameter — Behavior
tc Name: _BE_charity
Long Name: Elasticity of charitable contributions
Description: Defined as proportional change in cash and non-cash charitable contributions divided by proportional change in the after-tax price of charitable contributions caused by the reform. Must be zero or negative.
Default Value:
   for: [AGI < 50k, 50k <= AGI < 100k, 100k <= AGI]
2013: [0.0, 0.0, 0.0]

Back to Section Contents

diff --git a/taxcalc/behavior.json b/taxcalc/behavior.json index 7376f9f8c..93259358f 100644 --- a/taxcalc/behavior.json +++ b/taxcalc/behavior.json @@ -31,6 +31,22 @@ "validations": {"min": 0.0} }, + "_BE_subinc_wrt_earnings": { + "long_name": "Income and substitution elasticities apply to earnings", + "description": "False implies elasticities apply to taxable income; true implies they are both computed as proportional change in marginal net-of-tax rate (1-MTR) on taxpayer earnings caused by the reform and apply to taxpayer earnings.", + "section_1": "Behavior", + "section_2": "Taxable Income", + "notes": "", + "row_var": "FLPDYR", + "row_label": ["2013"], + "start_year": 2013, + "cpi_inflated": false, + "col_var": "", + "col_label": "", + "value": [false], + "validations": {"min": false, "max": true} + }, + "_BE_cg": { "long_name": "Semi-elasticity of long-term capital gains", "description": "Defined as change in logarithm of long-term capital gains divided by change in marginal tax rate (MTR) on long-term capital gains caused by the reform. Must be zero or negative. Read Behavior.response documentation for discussion of appropriate values.", diff --git a/taxcalc/behavior.py b/taxcalc/behavior.py index e033e60cf..5791b71a6 100644 --- a/taxcalc/behavior.py +++ b/taxcalc/behavior.py @@ -144,7 +144,7 @@ def response(calc_x, calc_y): Using this method, a semi-elasticity of -3.45 corresponds to a tax rate elasticity of -0.792. """ - # pylint: disable=too-many-statements,too-many-locals + # pylint: disable=too-many-statements,too-many-locals,too-many-branches assert calc_x.records.dim == calc_y.records.dim assert calc_x.records.current_year == calc_y.records.current_year # calculate sum of substitution and income effects @@ -152,27 +152,41 @@ def response(calc_x, calc_y): sub = np.zeros(calc_x.records.dim) inc = np.zeros(calc_x.records.dim) else: - # calculate marginal tax rates on wages and combined taxes + # calculate marginal combined tax rates on taxpayer wages+salary # (e00200p is taxpayer's wages+salary) wage_mtr_x, wage_mtr_y = Behavior._mtr_xy(calc_x, calc_y, mtr_of='e00200p', tax_type='combined') - # calculate magnitude of substitution and income effects + # calculate magnitude of substitution effect if calc_y.behavior.BE_sub == 0.0: sub = np.zeros(calc_x.records.dim) else: - # proportional change in marginal net-of-tax rates on wages - # (c04800 is filing unit's taxable income) + # proportional change in marginal net-of-tax rates on earnings pch = ((1. - wage_mtr_y) / (1. - wage_mtr_x)) - 1. - sub = calc_y.behavior.BE_sub * pch * calc_x.records.c04800 + if calc_y.behavior.BE_subinc_wrt_earnings: + # Note: e00200 is filing unit's wages+salaries + sub = calc_y.behavior.BE_sub * pch * calc_x.records.e00200 + else: + # Note: c04800 is filing unit's taxable income + sub = calc_y.behavior.BE_sub * pch * calc_x.records.c04800 + # calculate magnitude of income effect if calc_y.behavior.BE_inc == 0.0: inc = np.zeros(calc_x.records.dim) else: - # dollar change in after-tax income - # (combined is filing unit's income+payroll tax liability) - dch = calc_x.records.combined - calc_y.records.combined - inc = calc_y.behavior.BE_inc * dch - taxinc_chg = sub + inc + if calc_y.behavior.BE_subinc_wrt_earnings: + # proportional change in after-tax income + with np.errstate(invalid='ignore'): + pch = np.where(calc_x.records.aftertax_income > 0., + (calc_y.records.aftertax_income / + calc_x.records.aftertax_income) - 1., + 0.) + inc = calc_y.behavior.BE_inc * pch * calc_x.records.e00200 + else: + # dollar change in after-tax income + # Note: combined is f.unit's income+payroll tax liability + dch = calc_x.records.combined - calc_y.records.combined + inc = calc_y.behavior.BE_inc * dch + si_chg = sub + inc # calculate long-term capital-gains effect if calc_y.behavior.BE_cg == 0.0: ltcg_chg = np.zeros(calc_x.records.dim) @@ -253,7 +267,11 @@ def response(calc_x, calc_y): nc_charity_chg) # Add behavioral-response changes to income sources calc_y_behv = copy.deepcopy(calc_y) - calc_y_behv = Behavior._update_ordinary_income(taxinc_chg, calc_y_behv) + if calc_y_behv.behavior.BE_subinc_wrt_earnings: + calc_y_behv.records.e00200 = calc_y_behv.records.e00200 + si_chg + calc_y_behv.records.e00200p = calc_y_behv.records.e00200p + si_chg + else: + calc_y_behv = Behavior._update_ordinary_income(si_chg, calc_y_behv) calc_y_behv = Behavior._update_cap_gain_income(ltcg_chg, calc_y_behv) calc_y_behv = Behavior._update_charity(c_charity_chg, nc_charity_chg, calc_y_behv) @@ -286,7 +304,7 @@ def _validate_elasticity_values(self): elif elast == '_BE_charity': if val > 0.0: raise ValueError(msg.format(elast, neg, year, val)) - else: + elif elast != '_BE_subinc_wrt_earnings': raise ValueError('illegal elasticity {}'.format(elast)) @staticmethod diff --git a/taxcalc/responses/economic_responses_template.json b/taxcalc/responses/economic_responses_template.json index 4f1987b0b..c33106fa3 100644 --- a/taxcalc/responses/economic_responses_template.json +++ b/taxcalc/responses/economic_responses_template.json @@ -53,6 +53,7 @@ "behavior": { "_BE_inc": {"2017": [0.0]}, "_BE_sub": {"2017": [0.0]}, + "_BE_subinc_wrt_earnings": {"2017": [false]}, "_BE_cg": {"2017": [0.0]}, "_BE_charity": {"2017": [[0.0, 0.0, 0.0]]} }, diff --git a/taxcalc/tests/test_behavior.py b/taxcalc/tests/test_behavior.py index 085c1d41c..a5c463ef5 100644 --- a/taxcalc/tests/test_behavior.py +++ b/taxcalc/tests/test_behavior.py @@ -48,12 +48,14 @@ def test_behavioral_response_Calculator(cps_subsample): '_BE_charity': [[0.0, 0.0, 0.0]]}} behavior_y.update_behavior(behavior0) calc_y_behavior0 = Behavior.response(calc_x, calc_y) - behavior1 = {year: {'_BE_sub': [0.3], '_BE_cg': [0.0]}} + behavior1 = {year: {'_BE_sub': [0.3], '_BE_inc': [-0.1], '_BE_cg': [0.0], + '_BE_subinc_wrt_earnings': [True]}} behavior_y.update_behavior(behavior1) assert behavior_y.has_response() is True - assert behavior_y.BE_sub == 0.3 - assert behavior_y.BE_inc == 0.0 - assert behavior_y.BE_cg == 0.0 + epsilon = 1e-9 + assert abs(behavior_y.BE_sub - 0.3) < epsilon + assert abs(behavior_y.BE_inc - -0.1) < epsilon + assert abs(behavior_y.BE_cg - 0.0) < epsilon calc_y_behavior1 = Behavior.response(calc_x, calc_y) behavior2 = {year: {'_BE_sub': [0.5], '_BE_cg': [-0.8]}} behavior_y.update_behavior(behavior2)