Skip to content
Merged
1 change: 1 addition & 0 deletions changelog.d/fix-cbo-agi-2016-2018-soi.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fill total AGI in calibration.gov.cbo.income_by_source.adjusted_gross_income for 2015-2018 with IRS Statistics of Income actuals. The 2015 value was tax-year 2013 AGI excluding dependent filers and was held flat through 2018, so dollar inputs on the default uprating series (those with no series of their own, such as charitable donations) dated 2018 or earlier came out 13% to 29% too high when uprated to 2019 or later, and up to 12% too low when uprated to a year within 2016-2018. Medicare IRMAA's two-year lookback deflates income by the same per-capita index, so a household entered for a single benefit year from 2016 to 2020 now gets that year's income times 1.007, 0.942, 0.890, 0.932 or 0.929, instead of 1.009, 1.016, 1.014, 0.765 or 0.721.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
description: Historical data and projections of detailed adjusted gross income (AGI) by source (2019-2036)
description: Historical data and projections of detailed adjusted gross income (AGI) by source from CBO (2019-2036), with IRS Statistics of Income actuals for total AGI in 2015-2018
metadata:
propagate_metadata_to_children: true
unit: currency-USD
Expand All @@ -19,12 +19,33 @@ adjusted_gross_income:
metadata:
label: Total AGI
values:
# IRS Statistics of Income actuals, all returns: adjusted gross income
# less deficit.
2015-01-01:
value: 9_033_840_000_000
value: 10_210_310_102_000
metadata:
reference:
- title: Tax Foundation
href: https://taxfoundation.org/data/all/federal/summary-latest-federal-income-tax-data-2015-update/
- title: IRS SOI Tax Stats - Publication 1304 Table 1.1, tax year 2015, adjusted gross income less deficit
href: https://www.irs.gov/pub/irs-soi/15in11si.xls
2016-01-01:
value: 10_225_938_479_000
metadata:
reference:
- title: IRS SOI Tax Stats - Publication 1304 Table 1.1, tax year 2016, adjusted gross income less deficit
href: https://www.irs.gov/pub/irs-soi/16in11si.xls
2017-01-01:
value: 11_009_900_155_000
metadata:
reference:
- title: IRS SOI Tax Stats - Publication 1304 Table 1.1, tax year 2017, adjusted gross income less deficit
href: https://www.irs.gov/pub/irs-soi/17in11si.xls
2018-01-01:
value: 11_643_439_106_000
metadata:
reference:
- title: IRS SOI Tax Stats - Publication 1304 Table 1.1, tax year 2018, adjusted gross income less deficit
href: https://www.irs.gov/pub/irs-soi/18in11si.xls
# CBO from 2019.
2019-01-01: 11_962_000_000_000
2020-01-01: 12_650_000_000_000
2021-01-01: 14_791_339_000_000
Expand Down
20 changes: 20 additions & 0 deletions policyengine_us/tests/core/test_uprating_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,26 @@ def test_series_is_defined_across_the_projection_window(series):
assert _value(series, year) not in (None, 0)


def test_default_series_has_a_value_for_every_year():
"""A year with no value holds the previous one, so the per-capita index
drifts down with population and then jumps when values resume. CBO AGI
once held a tax-year 2013 total for 2015 and had nothing for 2016 to
2018. Inputs on the default series dated 2018 came out 29% too high by
2019, and Medicare IRMAA's lookback, which deflates income by the same
index, put a household entered for 2019 alone at 0.765 of its income
instead of 0.932. With the right 2015 total, the gap alone gives 14% and
0.864."""
total = get_parameter(system.parameters, _authored_series(DEFAULT))
years = sorted(
{
int(value.instant_str[:4])
for value in total.values_list
if value.instant_str.endswith("-01-01")
}
)
assert years == list(range(years[0], years[-1] + 1))


@pytest.mark.parametrize("year", (2025, 2030, 2035, 2036))
def test_unemployment_compensation_grows_with_cbo_unemployment_outlays(year):
soi = "calibration.gov.irs.soi.unemployment_compensation"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@
# holds no input for that year or any earlier one, the MAGI of the earliest
# year with inputs (no earlier than 2015, the first modeled year) stands in,
# deflated to the year two years prior by
# calibration.gov.cbo.income_by_source.adjusted_gross_income_per_capita (CBO
# calibration.gov.cbo.income_by_source.adjusted_gross_income_per_capita (total
# AGI divided by population). That index starts in 2015 and is held at its
# 2015 value before then:
# 2015: 9,033,840,000,000 / 326,078,000 = 27,704.54
# 2016: 9,033,840,000,000 / 328,964,000 = 27,461.49
# 2015 value before then. Total AGI is IRS SOI Publication 1304 Table 1.1, all
# returns, adjusted gross income less deficit, for 2015 to 2018
# (https://www.irs.gov/pub/irs-soi/15in11si.xls to 18in11si.xls), and CBO's
# series from 2019:
# 2015: 10,210,310,102,000 / 326,078,000 = 31,312.48
# 2016: 10,225,938,479,000 / 328,964,000 = 31,085.28
# 2017: 11,009,900,155,000 / 331,211,000 = 33,241.35
# 2018: 11,643,439,106,000 / 333,525,000 = 34,910.24
# 2019: 11,962,000,000,000 / 335,329,000 = 35,672.43
# 2022: 14,821,800,000,000 / 339,654,000 = 43,637.94
# 2023: 15,347,400,000,000 / 342,835,000 = 44,766.14
# 2024: 16,685,900,000,000 / 346,588,000 = 48,143.33
Expand All @@ -19,7 +25,7 @@
age: 66
employment_income: 500_000
output:
# 500,000 x 27,704.54 (2013, held at 2015) / 27,704.54 (2015).
# 500,000 x 31,312.48 (2013, held at 2015) / 31,312.48 (2015).
medicare_irmaa_magi_two_years_prior: 500_000

- name: Case 2, 2016 benefit year with only 2016 inputs uses 2016 income.
Expand All @@ -29,8 +35,8 @@
age: 66
employment_income: 500_000
output:
# 500,000 x 27,704.54 (2014, held at 2015) / 27,461.49 (2016).
medicare_irmaa_magi_two_years_prior: 504_425.34
# 500,000 x 31,312.48 (2014, held at 2015) / 31,085.28 (2016).
medicare_irmaa_magi_two_years_prior: 503_654.40

- name: Case 3, 2015 benefit year counts 2013 income that is provided.
period: 2015
Expand Down Expand Up @@ -87,10 +93,38 @@
is_medicare_eligible: true
medicare_enrolled: true
output:
# 100,000 x 27,704.54 / 27,461.49, under the first threshold of 103,001.
medicare_irmaa_magi_two_years_prior: 100_885.07
# 100,000 x 31,312.48 / 31,085.28, under the first threshold of 103,001.
medicare_irmaa_magi_two_years_prior: 100_730.88
income_adjusted_part_d_premium_surcharge: 0

- name: Case 8, 2018 benefit year with only 2018 inputs deflates 2018 income to 2016.
period: 2018
# MAGI is stored as float32, so allow for its rounding.
absolute_error_margin: 0.1
input:
age: 66
employment_income: 500_000
output:
# 500,000 x 31,085.28 (2016) / 34,910.24 (2018). Both years are IRS SOI
# totals. When total AGI had no 2016-2018 values, its 2015 value (tax-year
# 2013 AGI excluding dependent filers) was held, so the index fell with
# population (27,461.49 in 2016, 27,085.95 in 2018) and this came to
# 506,932.37.
medicare_irmaa_magi_two_years_prior: 445_217.19

- name: Case 9, 2019 benefit year with only 2019 inputs deflates 2019 income to 2017.
period: 2019
# MAGI is stored as float32, so allow for its rounding.
absolute_error_margin: 0.1
input:
age: 66
employment_income: 500_000
output:
# 500,000 x 33,241.35 (2017, IRS SOI) / 35,672.43 (2019, CBO). When total
# AGI for 2017 held the old 2015 value, the index was 27,275.18 and this
# came to 382,300.59.
medicare_irmaa_magi_two_years_prior: 465_924.93

- name: A 2025 household with only 2024 inputs has IRMAA set on deflated 2024 income.
period: 2025
absolute_error_margin: 0.01
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
members: [person]
state_code: TX
output:
# 10,000 x 27,704.54 (2013, held at 2015) / 27,704.54 (2015).
# 10,000 x 31,312.48 (2013, held at 2015) / 31,312.48 (2015).
medicare_irmaa_magi_two_years_prior: 10_000
income_tax: 0
ssi: 4_306
Expand All @@ -52,8 +52,9 @@
members: [person]
state_code: TX
output:
# 10,000 x 27,704.54 (2014, held at 2015) / 27,461.49 (2016).
medicare_irmaa_magi_two_years_prior: 10_088.51
# 10,000 x 31,312.48 (2014, held at 2015) / 31,085.28 (2016), from IRS
# SOI total AGI over population (see medicare_irmaa_magi_two_years_prior.yaml).
medicare_irmaa_magi_two_years_prior: 10_073.09
income_tax: 0
ssi: 4_306
snap: 192
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# charitable_cash_donations has no uprating series of its own, so it follows the
# default, calibration.gov.cbo.income_by_source.adjusted_gross_income_per_capita
# (total AGI divided by population). An input dated in one year and read in a
# later year is multiplied by the ratio of that index between the two years.
# Total AGI is IRS SOI Publication 1304 Table 1.1, all returns, adjusted gross
# income less deficit, for 2015 to 2018, and CBO's series from 2019:
# 2015: 10,210,310,102,000 / 326,078,000 = 31,312.48
# (https://www.irs.gov/pub/irs-soi/15in11si.xls)
# 2018: 11,643,439,106,000 / 333,525,000 = 34,910.24
# (https://www.irs.gov/pub/irs-soi/18in11si.xls)
# 2019: 11,962,000,000,000 / 335,329,000 = 35,672.43

- name: A 2018 donation read in 2019 grows with per-capita AGI across the SOI-to-CBO splice.
period: 2019
absolute_error_margin: 0.01
input:
charitable_cash_donations:
2018: 10_000
output:
# 10,000 x 35,672.43 / 34,910.24. When total AGI for 2018 held the old 2015
# value, the index was 27,085.95 and this came to 13,170.09.
charitable_cash_donations: 10_218.33

- name: A 2015 donation read in 2018 grows with per-capita AGI within the SOI years.
period: 2018
absolute_error_margin: 0.01
input:
charitable_cash_donations:
2015: 10_000
output:
# 10,000 x 34,910.24 / 31,312.48. When total AGI held the old 2015 value
# through 2018, the index was 27,704.54 in 2015 and 27,085.95 in 2018, and
# this came to 9,776.72.
charitable_cash_donations: 11_148.99
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
TAX_UNIT_ADDITIONS,
)

# Per-capita CBO AGI: the series that extends a single-year dataset's dollar
# inputs to later years when they have no series of their own
# (DEFAULT_MICRODATA_UPRATING, divided by population). Deflating by it runs
# that projection backward.
# Per-capita total AGI (IRS SOI for 2015-2018, CBO from 2019): the series that
# extends a single-year dataset's dollar inputs to later years when they have
# no series of their own (DEFAULT_MICRODATA_UPRATING, divided by population).
# Deflating by it runs that projection backward.
LOOKBACK_DEFLATOR = (
"calibration.gov.cbo.income_by_source.adjusted_gross_income_per_capita"
)
Expand Down Expand Up @@ -59,12 +59,12 @@ class medicare_irmaa_magi_two_years_prior(Variable):
"gov.simulation.medicare_irmaa_uses_current_year_income but "
"deflated. Provide this value, or inputs for the year two years "
"prior, to hold it fixed. "
"CBO AGI has no values for 2016 to 2018 and is held at its 2015 "
"value, so the index drifts down through 2018 and then rises 31% "
"from 2017 to 2019 and 39% from 2018 to 2020: a household entered "
"for 2016, 2017 or 2018 alone gets slightly more than that year's "
"income, and one entered for 2019 or 2020 alone gets that year's "
"income times 0.765 or 0.721. "
"Total AGI in that index is IRS Statistics of Income actuals for "
"2015 to 2018 and CBO's series from 2019. The index begins in 2015 "
"and is held at its 2015 value before then, so a household entered "
"for 2016 alone gets that year's income times the index for 2015 "
"over the index for 2016, slightly more than that income because "
"per-capita AGI fell from 2015 to 2016. "
"When inputs reach back to 2013 or 2014 and the year two years prior "
"is one of those years, its income cannot be computed: only income "
"provided for it counts, and the rest is zero."
Expand Down
Loading