Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to CPI_offset #2413

Merged
merged 14 commits into from
Jul 20, 2020
9 changes: 5 additions & 4 deletions taxcalc/calcfunctions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@

These functions are imported into the Calculator class.

Note: the CPI_offset policy parameter is the only policy parameter that
does not appear here; it is used in the policy.py file to possibly adjust
the price inflation rate used to index policy parameters (as would be done
in a reform that introduces chained-CPI indexing).
Note: the parameter_indexing_CPI_offset policy parameter is the only
policy parameter that does not appear here; it is used in the policy.py
file to possibly adjust the price inflation rate used to index policy
parameters (as would be done in a reform that introduces chained-CPI
indexing).
"""
# CODING-STYLE CHECKS:
# pycodestyle calcfunctions.py
Expand Down
119 changes: 91 additions & 28 deletions taxcalc/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import requests

import taxcalc
from taxcalc.growfactors import GrowFactors
from taxcalc.utils import json_to_dict


Expand Down Expand Up @@ -149,12 +150,18 @@ def adjust_with_indexing(self, params_or_path, **kwargs):
Custom adjust method that handles special indexing logic. The logic
is:

1. If "CPI_offset" is adjusted, revert all values of indexed parameters
to the 'known' values:
1. If "parameter_indexing_CPI_offset" is adjusted, first set
parameter_indexing_CPI_offset to zero before implementing the
adjusted parameter_indexing_CPI_offset to avoid stacking adjustments.
Then, revert all values of indexed parameters to the 'known' values:
a. The current values of parameters that are being adjusted are
deleted after the first year in which CPI_offset is adjusted.
deleted after the first year in which
parameter_indexing_CPI_offset is adjusted.
b. The current values of parameters that are not being adjusted
(i.e. are not in params) are deleted after the last known year.
(i.e. are not in params) are deleted after the last known year,
with the exception of parameters that revert to their pre-TCJA
values in 2026. Instead, these (2026) parameter values are
recalculated using the new inflation rates.
After the 'unknown' values have been deleted, the last known value
is extrapolated through the budget window. If there are indexed
parameters in the adjustment, they will be included in the final
Expand All @@ -171,17 +178,11 @@ def adjust_with_indexing(self, params_or_path, **kwargs):
parameter through the remaining years or until the -indexed
status changes again.
3. Update all parameters that are not indexing related, i.e. they are
not "CPI_offset" or do not end with "-indexed".
not "parameter_indexing_CPI_offset" or do not end with "-indexed".
4. Return parsed adjustment with all adjustments, including "-indexed"
parameters.

Notable side-effects:
- All values of indexed parameters, including default values, are
wiped out after the first year in which the "CPI_offset" is
changed. This is only necessary because Tax-Calculator
hard-codes inflated values. If Tax-Calculator only hard-coded
values that were changed for non-inflation related reasons,
then this would not be necessary for default values.
- All values of a parameter whose indexed status is adjusted are
wiped out after the year in which the value is adjusted for the
same hard-coding reason.
Expand All @@ -191,29 +192,46 @@ def adjust_with_indexing(self, params_or_path, **kwargs):
label_to_extend = self.label_to_extend
array_first = self.array_first
self.array_first = False
self._gfactors = GrowFactors()

params = self.read_params(params_or_path)

# Check if CPI_offset is adjusted. If so, reset values of all indexed
# parameters after year where CPI_offset is changed. If CPI_offset is
# changed multiple times, then reset values after the first year in
# which the CPI_offset is changed.
# Check if parameter_indexing_CPI_offset is adjusted. If so, reset
# values of all indexed parameters after year where
# parameter_indexing_CPI_offset is changed. If
# parameter_indexing_CPI_offset is changed multiple times, then
# reset values after the first year in which the
# parameter_indexing_CPI_offset is changed.
needs_reset = []
if params.get("CPI_offset") is not None:
# Update CPI_offset with new value.
if params.get("parameter_indexing_CPI_offset") is not None:
# Update parameter_indexing_CPI_offset with new value.
cpi_adj = super().adjust(
{"CPI_offset": params["CPI_offset"]}, **kwargs
{"parameter_indexing_CPI_offset":
params["parameter_indexing_CPI_offset"]}, **kwargs
)
# turn off extend now that CPI_offset has been updated.
# turn off extend now that parameter_indexing_CPI_offset
# has been updated.
self.label_to_extend = None
# Get first year in which CPI_offset is changed.
# Get first year in which parameter_indexing_CPI_offset
# is changed.
cpi_min_year = min(
cpi_adj["CPI_offset"], key=lambda vo: vo["year"]
cpi_adj["parameter_indexing_CPI_offset"],
key=lambda vo: vo["year"]
)
# Apply new CPI_offset values to inflation rates

rate_adjustment_vals = self.select_gte(
"CPI_offset", year=cpi_min_year["year"]
"parameter_indexing_CPI_offset", year=cpi_min_year["year"]
)
# "Undo" any existing parameter_indexing_CPI_offset for
# years after parameter_indexing_CPI_offset has
# been updated.
self._inflation_rates = self._inflation_rates[
:cpi_min_year["year"] - self.start_year
] + self._gfactors.price_inflation_rates(
cpi_min_year["year"], self.LAST_BUDGET_YEAR)

# Then apply new parameter_indexing_CPI_offset values to
# inflation rates
for cpi_vo in rate_adjustment_vals:
self._inflation_rates[
cpi_vo["year"] - self.start_year
Expand All @@ -223,7 +241,10 @@ def adjust_with_indexing(self, params_or_path, **kwargs):
init_vals = {}
to_delete = {}
for param in params:
if param == "CPI_offset" or param in self._wage_indexed:
if (
param == "parameter_indexing_CPI_offset" or
param in self._wage_indexed
):
continue
if param.endswith("-indexed"):
param = param.split("-indexed")[0]
Expand All @@ -241,13 +262,55 @@ def adjust_with_indexing(self, params_or_path, **kwargs):
super().adjust(init_vals, **kwargs)
hdoupe marked this conversation as resolved.
Show resolved Hide resolved

# 1.b For all others, these are years after last_known_year.
last_known_year = max(cpi_min_year["year"], self._last_known_year)
# calculate 2026 value, using new inflation rates, for parameters
# that revert to their pre-TCJA values.
long_params = ['II_brk7', 'II_brk6', 'II_brk5', 'II_brk4',
'II_brk3', 'II_brk2', 'II_brk1',
'PT_brk7', 'PT_brk6', 'PT_brk5', 'PT_brk4',
'PT_brk3', 'PT_brk2', 'PT_brk1',
'PT_qbid_taxinc_thd',
'ALD_BusinessLosses_c',
'STD', 'II_em', 'II_em_ps',
'AMT_em', 'AMT_em_ps', 'AMT_em_pe',
'ID_ps', 'ID_AllTaxes_c']
final_ifactor = 1.0
pyear = 2017 # prior year before TCJA first implemented
fyear = 2026 # final year in which parameter values revert to
# pre-TCJA values
# construct final-year inflation factor from prior year
# NOTE: pvalue[t+1] = pvalue[t] * ( 1 + irate[t] )
for year in range(pyear, fyear):
final_ifactor *= 1 + \
self._inflation_rates[year - self.start_year]

long_param_vals = defaultdict(list)
# compute final year parameter value
for param in long_params:
# only revert param in 2026 if it's not in revision
if params.get(param) is None:
# grab param values from 2017
vos = self.select_eq(param, year=pyear)
# use final_ifactor to inflate from 2017 to 2026
for vo in vos:
long_param_vals[param].append(
# Create new dict to avoid modifying the original
dict(
vo,
value=min(9e99, round(
vo["value"] * final_ifactor, 0)),
year=fyear,
)
)
needs_reset.append(param)
super().adjust(long_param_vals, **kwargs)

init_vals = {}
to_delete = {}
last_known_year = max(cpi_min_year["year"], self._last_known_year)
for param in self._data:
if (
param in params or
param == "CPI_offset" or
param == "parameter_indexing_CPI_offset" or
param in self._wage_indexed
):
continue
Expand All @@ -257,8 +320,8 @@ def adjust_with_indexing(self, params_or_path, **kwargs):
True,
{"year": last_known_year}
)
to_delete[param] = self.select_gt(
param, year=last_known_year
to_delete[param] = self.select_eq(
param, strict=True, _auto=True
)
needs_reset.append(param)

Expand Down
18 changes: 14 additions & 4 deletions taxcalc/policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,14 @@ class instance: Policy
'FilerCredit_c': 'is a removed parameter name',
'ALD_InvInc_ec_base_RyanBrady': 'is a removed parameter name',
# TODO: following parameter renamed in PR 2292 merged on 2019-04-15
'cpi_offset': 'was renamed CPI_offset in release 2.0.0',
"cpi_offset": (
"was renamed parameter_indexing_CPI_offset. "
"See documentation for change in usage."
),
"CPI_offset": (
"was renamed parameter_indexing_CPI_offset. "
"See documentation for change in usage."
),
# TODO: following parameters renamed in PR 2345 merged on 2019-06-24
'PT_excl_rt':
'was renamed PT_qbid_rt in release 2.4.0',
Expand Down Expand Up @@ -124,9 +131,12 @@ def parameter_list():

def set_rates(self):
"""Initialize taxcalc indexing data."""
cpi_vals = [vo["value"] for vo in self._data["CPI_offset"]["value"]]
# extend cpi_offset values through budget window if they
# have not been extended already.
cpi_vals = [
vo["value"] for
vo in self._data["parameter_indexing_CPI_offset"]["value"]
]
# extend parameter_indexing_CPI_offset values through budget window
# if they have not been extended already.
cpi_vals = cpi_vals + cpi_vals[-1:] * (
self.end_year - self.start_year + 1 - len(cpi_vals)
)
Expand Down
2 changes: 1 addition & 1 deletion taxcalc/policy_current_law.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
}
}
},
"CPI_offset": {
"parameter_indexing_CPI_offset": {
"title": "Decimal offset ADDED to unchained CPI to get parameter indexing rate",
"description": "Values are zero before 2017; reforms that introduce indexing with chained CPI would have values around -0.0025 beginning in the year before the first year policy parameters will have values computed with chained CPI.",
"notes": "See April 2013 CBO report entitled 'What Would Be the Effect on the Deficit of Using the Chained CPI to Index Benefit Programs and the Tax Code?', which includes this: 'The chained CPI grows more slowly than the traditional CPI does: an average of about 0.25 percentage points more slowly per year over the past decade.' <https://www.cbo.gov/publication/44089>",
Expand Down
4 changes: 2 additions & 2 deletions taxcalc/reforms/2017_law.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// - Set pre-TCJA above the line deduction policy (7)
// - Set pre-TCJA itemized deduction policy (8)
// Reform_Parameter_Map:
// - 0: CPI_offset
// - 0: parameter_indexing_CPI_offset
// - 1: II_rt?/II_brk? and PT_rt?/PT_brk?
// - 2: six PT_qbid_* parameters
// - 3: STD and II_em parameters
Expand All @@ -26,7 +26,7 @@
// NOTE: this reform projects pre-TCJA 2017 parameter values forward using the
// unchained CPI-U price index.
{
"CPI_offset": {"2017": 0.0025},
"parameter_indexing_CPI_offset": {"2017": 0},
"II_rt1": {"2018": 0.10},
"II_brk1": {"2017": [9325, 18650, 9325, 13350, 18650]},
"II_rt2": {"2018": 0.15},
Expand Down
4 changes: 2 additions & 2 deletions taxcalc/reforms/TCJA.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
// - 6: AMT_em*
// - 7: ALD_*
// - 8: ID_* (can safely ignore WARNINGs about values for several parameters)
// - 9: CPI_offset
// - 9: parameter_indexing_CPI_offset
// Note: II_brk*, PT_brk*, STD, II_em are rounded to nearest integer value
{
"II_rt1": {"2018": 0.10,
Expand Down Expand Up @@ -153,5 +153,5 @@
"2026": 0},
"ID_Medical_frt": {"2017": 0.075,
"2019": 0.075},
"CPI_offset": {"2017": -0.0025}
"parameter_indexing_CPI_offset": {"2017": -0.0025}
}
2 changes: 1 addition & 1 deletion taxcalc/tests/reforms.json
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@
"56": {
"baseline": "policy_current_law.json",
"start_year": 2017,
"value": {"CPI_offset": 0.0025},
"value": {"parameter_indexing_CPI_offset": 0},
"name": "Repeal TCJA chained CPI indexing",
"output_type": "iitax",
"compare_with": {}
Expand Down
Loading