Skip to content

Commit

Permalink
Merge pull request #1554 from martinholmer/add-new-behavior
Browse files Browse the repository at this point in the history
Add _BE_subinc_wrt_earnings Behavior parameter
  • Loading branch information
martinholmer authored Sep 20, 2017
2 parents f9a8334 + a3d00c3 commit cd70f49
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 18 deletions.
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ <h3 id="params-consump">Consumption Parameters</h3>

<h3 id="params-behavior">Behavior Parameters</h3>

<p><b>Behavior &mdash; Taxable Income</b><br><i>tc Name:</i> _BE_inc<br><i>TB Name:</i> Income elasticity of taxable income<br><i>Description:</i> Defined as dollar change in taxable income divided by dollar change in after-tax income caused by the reform. Must be zero or negative.<br><i>Default Value:</i><br>2013: 0.0</p><p><b>Behavior &mdash; Taxable Income</b><br><i>tc Name:</i> _BE_sub<br><i>TB Name:</i> Substitution elasticity of taxable income<br><i>Description:</i> 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.<br><i>Default Value:</i><br>2013: 0.0</p><p><b>Behavior &mdash; Capital Gains</b><br><i>tc Name:</i> _BE_cg<br><i>TB Name:</i> Semi-elasticity of long-term capital gains<br><i>Description:</i> 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.<br><i>Default Value:</i><br>2013: 0.0</p><p><b>Response Parameter &mdash; Behavior</b><br><i>tc Name:</i> _BE_charity<br><i>Long Name:</i> Elasticity of charitable contributions<br><i>Description:</i> 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.<br><i>Default Value:</i><br>&nbsp;&nbsp; for: [AGI < 50k, 50k <= AGI < 100k, 100k <= AGI]<br>2013: [0.0, 0.0, 0.0]</p>
<p><b>Behavior &mdash; Taxable Income</b><br><i>tc Name:</i> _BE_inc<br><i>TB Name:</i> Income elasticity of taxable income<br><i>Description:</i> Defined as dollar change in taxable income divided by dollar change in after-tax income caused by the reform. Must be zero or negative.<br><i>Default Value:</i><br>2013: 0.0</p><p><b>Behavior &mdash; Taxable Income</b><br><i>tc Name:</i> _BE_sub<br><i>TB Name:</i> Substitution elasticity of taxable income<br><i>Description:</i> 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.<br><i>Default Value:</i><br>2013: 0.0</p><p><b>Behavior &mdash; Taxable Income</b><br><i>tc Name:</i> _BE_subinc_wrt_earnings<br><i>TB Name:</i> Income and substitution elasticities apply to earnings<br><i>Description:</i> 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.<br><i>Default Value:</i><br>2013: False</p><p><b>Behavior &mdash; Capital Gains</b><br><i>tc Name:</i> _BE_cg<br><i>TB Name:</i> Semi-elasticity of long-term capital gains<br><i>Description:</i> 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.<br><i>Default Value:</i><br>2013: 0.0</p><p><b>Response Parameter &mdash; Behavior</b><br><i>tc Name:</i> _BE_charity<br><i>Long Name:</i> Elasticity of charitable contributions<br><i>Description:</i> 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.<br><i>Default Value:</i><br>&nbsp;&nbsp; for: [AGI < 50k, 50k <= AGI < 100k, 100k <= AGI]<br>2013: [0.0, 0.0, 0.0]</p>

<p><a href="#params">Back to Section Contents</a></p>

Expand Down
16 changes: 16 additions & 0 deletions taxcalc/behavior.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
44 changes: 31 additions & 13 deletions taxcalc/behavior.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,35 +144,49 @@ 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
if calc_y.behavior.BE_sub == 0.0 and calc_y.behavior.BE_inc == 0.0:
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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions taxcalc/responses/economic_responses_template.json
Original file line number Diff line number Diff line change
Expand Up @@ -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]]}
},
Expand Down
10 changes: 6 additions & 4 deletions taxcalc/tests/test_behavior.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit cd70f49

Please sign in to comment.