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)