diff --git a/changelog.d/cbo-medicare-net-of-premiums.fixed.md b/changelog.d/cbo-medicare-net-of-premiums.fixed.md new file mode 100644 index 00000000000..31b17d9bb0c --- /dev/null +++ b/changelog.d/cbo-medicare-net-of-premiums.fixed.md @@ -0,0 +1 @@ +The default CBO household income now counts Medicare as CBO does, at the average cost per enrollee net of premiums and excluding the Part D low-income subsidy, through a new `cbo_medicare_benefit` variable instead of gross Medicare spending. diff --git a/policyengine_us/parameters/calibration/gov/hhs/medicare/part_d_low_income_subsidy_per_enrollee.yaml b/policyengine_us/parameters/calibration/gov/hhs/medicare/part_d_low_income_subsidy_per_enrollee.yaml new file mode 100644 index 00000000000..6e84c8d8cac --- /dev/null +++ b/policyengine_us/parameters/calibration/gov/hhs/medicare/part_d_low_income_subsidy_per_enrollee.yaml @@ -0,0 +1,54 @@ +description: The Medicare Trustees report and project these Part D low-income subsidy payments per Medicare enrollee. +metadata: + unit: currency-USD + period: year + label: Medicare Part D low-income subsidy per enrollee + # From 2036, each value is the 2035 value times the growth in Medicare + # spending per enrollee, so the subsidy keeps its 2035 share of spending. + uprating: calibration.gov.hhs.medicare.per_capita_cost + reference: + # Each value is Table IV.B10's "Low-income subsidy" payments ($ billions, + # incurred basis) over Table V.B3's total enrollment (file page 196, + # thousands), people with Part A and/or Part B coverage, so it is per + # Medicare enrollee, not per subsidy recipient. 2016 to 2035 come from the + # 2026 report's Table IV.B10 (file page 159); 2015, which that table no + # longer shows, from the 2025 report's (file page 159). The two reports + # agree on 2016 to 2023. 2026 Medicare Trustees Report, transmitted + # 2026-06-09, retrieved 2026-10-11, sha256 + # ffa56b9137006872300b0346149eae1613d09a172b6ba118aad48e66dfc48fa8; 2025 + # report sha256 + # 1b3de7a4faeee5ad42059e10740603182b716a5fa7b53b30d360b45b9e474d95. + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table IV.B10 + href: https://www.cms.gov/oact/tr/2026#page=159 + - title: 2025 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table IV.B10 + href: https://www.cms.gov/oact/tr/2025#page=159 + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table V.B3 + href: https://www.cms.gov/oact/tr/2026#page=196 +values: + # Historical. + 2015-01-01: 460.52 + 2016-01-01: 462.57 + 2017-01-01: 465.21 + 2018-01-01: 474.84 + 2019-01-01: 482.65 + 2020-01-01: 524.75 + 2021-01-01: 547.05 + 2022-01-01: 604.61 + 2023-01-01: 663.6 + 2024-01-01: 605.29 + # 2025 is the first year of the full Inflation Reduction Act benefit + # redesign (file page 119). The subsidy per low-income enrollee fell from + # $2,833 to $1,226 while the direct subsidy per enrollee rose from $383 to + # $1,869 (Table IV.B9, file page 158). + 2025-01-01: 251.12 + # Trustees' intermediate estimates. + 2026-01-01: 255.86 + 2027-01-01: 283.45 + 2028-01-01: 298.54 + 2029-01-01: 324.78 + 2030-01-01: 377.29 + 2031-01-01: 396.02 + 2032-01-01: 404.71 + 2033-01-01: 409.83 + 2034-01-01: 417.15 + 2035-01-01: 431.59 diff --git a/policyengine_us/parameters/calibration/gov/hhs/medicare/premium_income_per_enrollee/part_a.yaml b/policyengine_us/parameters/calibration/gov/hhs/medicare/premium_income_per_enrollee/part_a.yaml new file mode 100644 index 00000000000..5049d213595 --- /dev/null +++ b/policyengine_us/parameters/calibration/gov/hhs/medicare/premium_income_per_enrollee/part_a.yaml @@ -0,0 +1,47 @@ +description: The Medicare Trustees report and project this Part A premium income per Medicare enrollee, which counts premiums paid by or on behalf of people without premium-free Part A who enroll voluntarily. +metadata: + unit: currency-USD + period: year + label: Medicare Part A premium income per enrollee + # From 2036, each value is the 2035 value times the growth in Medicare + # spending per enrollee, so premiums keep their 2035 share of spending. + uprating: calibration.gov.hhs.medicare.per_capita_cost + reference: + # Each value is Table III.B4's "Premiums from voluntary enrollees" (file + # page 64, $ billions, cash basis) over Table V.B3's total enrollment + # (file page 196, thousands), which footnote 2 defines as the "Number of + # beneficiaries with HI and/or SMI coverage". The Part A premium is paid + # "by or on behalf of" these enrollees (glossary, file page 257), so state + # Medicaid buy-in payments are included. 2026 Medicare Trustees Report, + # transmitted 2026-06-09, retrieved 2026-10-11, sha256 + # ffa56b9137006872300b0346149eae1613d09a172b6ba118aad48e66dfc48fa8. + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table III.B4 + href: https://www.cms.gov/oact/tr/2026#page=64 + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table V.B3 + href: https://www.cms.gov/oact/tr/2026#page=196 + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, glossary, Part A premium + href: https://www.cms.gov/oact/tr/2026#page=257 +values: + # Historical. + 2015-01-01: 57.57 + 2016-01-01: 57.82 + 2017-01-01: 59.64 + 2018-01-01: 59.98 + 2019-01-01: 63.38 + 2020-01-01: 63.61 + 2021-01-01: 65.65 + 2022-01-01: 69.05 + 2023-01-01: 70.56 + 2024-01-01: 70.52 + 2025-01-01: 82.26 + # Trustees' intermediate estimates. + 2026-01-01: 97.54 + 2027-01-01: 102.32 + 2028-01-01: 106.72 + 2029-01-01: 112.68 + 2030-01-01: 117.5 + 2031-01-01: 125.13 + 2032-01-01: 131.5 + 2033-01-01: 140.4 + 2034-01-01: 149.07 + 2035-01-01: 156.27 diff --git a/policyengine_us/parameters/calibration/gov/hhs/medicare/premium_income_per_enrollee/part_b.yaml b/policyengine_us/parameters/calibration/gov/hhs/medicare/premium_income_per_enrollee/part_b.yaml new file mode 100644 index 00000000000..53b67c103aa --- /dev/null +++ b/policyengine_us/parameters/calibration/gov/hhs/medicare/premium_income_per_enrollee/part_b.yaml @@ -0,0 +1,61 @@ +description: The Medicare Trustees report and project this Part B premium income per Medicare enrollee, which counts premiums paid by or on behalf of enrollees, including income-related premiums and premiums that state Medicaid programs pay. +metadata: + unit: currency-USD + period: year + label: Medicare Part B premium income per enrollee + # From 2036, each value is the 2035 value times the growth in Medicare + # spending per enrollee, so premiums keep their 2035 share of spending. + uprating: calibration.gov.hhs.medicare.per_capita_cost + reference: + # Each value is premium income over Table V.B3's total enrollment (file + # page 196, thousands), people with Part A and/or Part B coverage. + # - 2015 to 2026: Table III.C7 (file page 102), "Premium income", $ + # millions, incurred basis. + # - 2027 to 2035: Table III.C4 (file page 96), "Premium income", $ + # billions, cash basis. Footnote 7 credits premiums withheld from the + # benefits due January 3, 2027 on December 31, 2026; their amount is + # III.C4's 2026 cash value less III.C7's 2026 incurred value (174.8 - + # 173.228 = 1.572), which is added back to 2027. Cash and incurred + # premium income agree in every year from 2017 to 2025 except 2020 and + # 2021, the other footnote 7 years. + # Premium income is "premium payments by (or on behalf of) enrollees" + # (file page 88). It includes income-related premiums: in 2019 the + # standard premium, $135.50 a month (Table III.C2, file page 89), times + # 56.019 million Part B enrollees plus Table V.E3's $8.4 billion of + # income-related premiums (file page 215) is $99.5 billion, against $99.4 + # billion of premium income. + # 2026 Medicare Trustees Report, transmitted 2026-06-09, retrieved + # 2026-10-11, sha256 + # ffa56b9137006872300b0346149eae1613d09a172b6ba118aad48e66dfc48fa8. + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table III.C7 + href: https://www.cms.gov/oact/tr/2026#page=102 + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table III.C4 + href: https://www.cms.gov/oact/tr/2026#page=96 + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table V.B3 + href: https://www.cms.gov/oact/tr/2026#page=196 + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Part B premium income + href: https://www.cms.gov/oact/tr/2026#page=88 +values: + # Historical. + 2015-01-01: 1_214.54 + 2016-01-01: 1_296.34 + 2017-01-01: 1_389.19 + 2018-01-01: 1_554.68 + 2019-01-01: 1_615.55 + 2020-01-01: 1_729.23 + 2021-01-01: 1_772.6 + 2022-01-01: 2_009.35 + 2023-01-01: 1_969.28 + 2024-01-01: 2_054.43 + 2025-01-01: 2_169.44 + # Trustees' intermediate estimates. + 2026-01-01: 2_448.69 + 2027-01-01: 2_549.32 + 2028-01-01: 2_755.79 + 2029-01-01: 2_946.87 + 2030-01-01: 3_178.93 + 2031-01-01: 3_409.4 + 2032-01-01: 3_662.85 + 2033-01-01: 3_985.68 + 2034-01-01: 4_334.37 + 2035-01-01: 4_652 diff --git a/policyengine_us/parameters/calibration/gov/hhs/medicare/premium_income_per_enrollee/part_d.yaml b/policyengine_us/parameters/calibration/gov/hhs/medicare/premium_income_per_enrollee/part_d.yaml new file mode 100644 index 00000000000..1c8af7fff7f --- /dev/null +++ b/policyengine_us/parameters/calibration/gov/hhs/medicare/premium_income_per_enrollee/part_d.yaml @@ -0,0 +1,57 @@ +description: The Medicare Trustees report and project this Part D premium income per Medicare enrollee, which counts premiums withheld from benefits, paid to the federal government or paid directly to plans, including income-related premiums. +metadata: + unit: currency-USD + period: year + label: Medicare Part D premium income per enrollee + # From 2036, each value is the 2035 value times the growth in Medicare + # spending per enrollee, so premiums keep their 2035 share of spending. + uprating: calibration.gov.hhs.medicare.per_capita_cost + reference: + # Each value is Table III.D3's "Premium income" (file page 116, $ + # billions, cash basis) over Table V.B3's total enrollment (file page 196, + # thousands), people with Part A and/or Part B coverage. Footnote 1: + # "Premiums include both amounts withheld from Social Security benefits or + # other Federal payments, direct payments to the Federal government, and + # those paid directly to Part D plans." Footnote 6 (file page 117) credits + # one January's withheld premiums on December 31 of the year before: about + # $0.2 billion in 2015, $0.1 billion in 2020 and an estimated $0.2 billion + # in 2026, which are moved to 2016, 2021 and 2027. Premium income includes + # income-related premiums, which are deposited into the Part D account + # (footnote 73, file page 151). So each value equals the Part D enrollment + # share times calibration.gov.hhs.medicare.part_d.premiums_per_enrollee, + # plus Table V.E4's aggregate income-related premiums (file page 218) per + # Medicare enrollee. 2026 Medicare Trustees Report, transmitted + # 2026-06-09, retrieved 2026-10-11, sha256 + # ffa56b9137006872300b0346149eae1613d09a172b6ba118aad48e66dfc48fa8. + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table III.D3 + href: https://www.cms.gov/oact/tr/2026#page=116 + - title: 2026 Annual Report of the Boards of Trustees of the Federal Hospital Insurance and Federal Supplementary Medical Insurance Trust Funds, Table V.B3 + href: https://www.cms.gov/oact/tr/2026#page=196 +values: + # Historical. + 2015-01-01: 224.86 + 2016-01-01: 245.3 + 2017-01-01: 264.13 + 2018-01-01: 264.91 + 2019-01-01: 255.14 + 2020-01-01: 249.65 + 2021-01-01: 267.27 + 2022-01-01: 270.08 + 2023-01-01: 274.75 + 2024-01-01: 282.08 + # 2025 premiums fell with the voluntary Part D premium demonstration for + # stand-alone prescription drug plans (file page 109). + 2025-01-01: 215.04 + # Trustees' intermediate estimates. The 2030 jump is the Inflation + # Reduction Act's reset of the base beneficiary premium percentage + # (file page 154). + 2026-01-01: 281.3 + 2027-01-01: 356.74 + 2028-01-01: 389.05 + 2029-01-01: 404.32 + 2030-01-01: 674.95 + 2031-01-01: 708.2 + 2032-01-01: 720.06 + 2033-01-01: 752.61 + 2034-01-01: 776.68 + 2035-01-01: 809.85 diff --git a/policyengine_us/parameters/gov/household/cbo_social_insurance_benefits.yaml b/policyengine_us/parameters/gov/household/cbo_social_insurance_benefits.yaml index ac597fb7156..550ea17a0f1 100644 --- a/policyengine_us/parameters/gov/household/cbo_social_insurance_benefits.yaml +++ b/policyengine_us/parameters/gov/household/cbo_social_insurance_benefits.yaml @@ -2,7 +2,7 @@ description: Social insurance benefits included in CBO household income. values: 0000-01-01: - social_security - - medicare_gross_cost + - cbo_medicare_benefit - unemployment_compensation - workers_compensation @@ -10,3 +10,16 @@ metadata: unit: list period: year label: CBO social insurance benefits + reference: + # "Social insurance benefits consist of benefits provided through Social + # Security (Old Age, Survivors, and Disability Insurance); Medicare + # (measured as the average cost to the government of providing those + # benefits, net of offsetting receipts); regular unemployment insurance + # ...; and workers' compensation." (Appendix A, note 3.) + - title: Congressional Budget Office, The Distribution of Household Income in 2021, Appendix A + href: https://www.cbo.gov/system/files/2024-09/60341-income.pdf#page=27 + # "The measure of Medicare benefits in The Distribution of Household + # Income includes the federal government's average cost per enrollee of + # providing Medicare, net of premiums paid by enrollees." + - title: Congressional Budget Office, Sensitivity of the Distribution of Household Income to the Treatment of Health Insurance From 1979 to 2021 + href: https://www.cbo.gov/publication/61910 diff --git a/policyengine_us/tests/core/test_cbo_medicare_benefit_series.py b/policyengine_us/tests/core/test_cbo_medicare_benefit_series.py new file mode 100644 index 00000000000..c3da92714a1 --- /dev/null +++ b/policyengine_us/tests/core/test_cbo_medicare_benefit_series.py @@ -0,0 +1,220 @@ +"""The per-enrollee series that ``cbo_medicare_benefit`` subtracts. + +CBO counts Medicare in household income at the government's average cost +per enrollee, net of premiums paid by enrollees, and counts the Part D +low-income subsidy separately. ``cbo_medicare_benefit`` takes CMS Medicare +spending per enrollee (``calibration.gov.hhs.medicare.per_capita_cost``) and +subtracts four series under ``calibration.gov.hhs.medicare``, each an +aggregate from the 2026 Medicare Trustees Report over Table V.B3's total +enrollment (people with Part A and/or Part B): + +- ``premium_income_per_enrollee.part_a``: Table III.B4 premiums from + voluntary enrollees. +- ``premium_income_per_enrollee.part_b``: Table III.C7 incurred premium + income through 2026, then Table III.C4 cash premium income, with the + December 31, 2026 credit moved back to 2027. +- ``premium_income_per_enrollee.part_d``: Table III.D3 premium income with + footnote 6's December 31 credits moved back a year. +- ``part_d_low_income_subsidy_per_enrollee``: Table IV.B10 low-income + subsidy (the 2025 report for 2015). + +These tests read parameters only; the YAML tests cover the variable. + +Invariants: + +1. Transcription: every value equals its formula applied to the Trustees' + published cells, rounded as stored. +2. Conservation: each series times total enrollment returns its published + aggregate, within the cells' rounding. +3. Differential: the Part D series equals the Part D enrollment share times + ``part_d.premiums_per_enrollee`` (which excludes income-related premiums, + per Part D enrollee) plus Table V.E4's income-related premiums per + Medicare enrollee: two derivations from different cells agree. +4. Part B premium income includes income-related premiums: in 2019 the + standard premium times Part B enrollment plus Table V.E3's aggregate + income-related premiums reproduces it within 0.5 percent, while the + standard premium alone falls more than 5 percent short. +5. Bounds: every series is positive, and net Medicare spending per enrollee + stays between 0 and gross spending, in every year from 2015 to 2200. +6. Extension: from 2036 to 2100 each series grows with Medicare spending + per enrollee, so each keeps its 2035 share of spending; later years hold + the 2100 value. +7. Calendar years: every date in a year reads the January 1 value. +""" + +import pytest +from hypothesis import given +from hypothesis import strategies as st +from policyengine_core.parameters.operations.get_parameter import get_parameter + +from policyengine_us.system import system + +MEDICARE = "calibration.gov.hhs.medicare" +SERIES = { + "part_a": f"{MEDICARE}.premium_income_per_enrollee.part_a", + "part_b": f"{MEDICARE}.premium_income_per_enrollee.part_b", + "part_d": f"{MEDICARE}.premium_income_per_enrollee.part_d", + "lis": f"{MEDICARE}.part_d_low_income_subsidy_per_enrollee", +} +SPENDING = f"{MEDICARE}.per_capita_cost" +FIRST_YEAR, LAST_PROJECTED_YEAR, END_YEAR = 2015, 2035, 2100 +YEARS = range(FIRST_YEAR, LAST_PROJECTED_YEAR + 1) + + +def _cells(*values): + return dict(zip(YEARS, values)) + + +# 2026 Medicare Trustees Report (sha256 ffa56b91...8fa8), file pages. +# Table V.B3 (page 196), total enrollment, thousands. +TOTAL_ENROLLMENT = _cells( + *(55_589, 57_073, 58_683, 60_020, 61_535, 62_887, 63_980, 65_166), + *(66_606, 68_066, 69_289, 70_743, 72_322, 74_026, 75_436, 76_598), + *(77_521, 78_327, 79_058, 79_827, 80_632), +) +# Table III.B4 (page 64), premiums from voluntary enrollees, $ billions. +PART_A_PREMIUMS = _cells( + *(3.2, 3.3, 3.5, 3.6, 3.9, 4.0, 4.2, 4.5, 4.7, 4.8, 5.7), + *(6.9, 7.4, 7.9, 8.5, 9.0, 9.7, 10.3, 11.1, 11.9, 12.6), +) +# Table III.C7 (page 102), premium income, incurred basis, $ millions. +PART_B_INCURRED = dict( + zip( + range(2015, 2027), + ( + *(67_515, 73_986, 81_522, 93_312, 99_413, 108_746, 113_411), + *(130_941, 131_166, 139_837, 150_318, 173_228), + ), + ) +) +# Table III.C4 (page 96), premium income, cash basis, $ billions. +PART_B_CASH = _cells( + *(69.4, 72.1, 81.5, 93.3, 99.4, 111.2, 111.0, 130.9, 131.2, 139.8, 150.3), + *(174.8, 182.8, 204.0, 222.3, 243.5, 264.3, 286.9, 315.1, 346.0, 375.1), +) +# Table III.D3 (page 116), premium income, cash basis, $ billions, and +# footnote 6's December 31 credits (page 117). +PART_D_CASH = _cells( + *(12.7, 13.8, 15.5, 15.9, 15.7, 15.8, 17.0, 17.6, 18.3, 19.2, 14.9), + *(20.1, 25.6, 28.8, 30.5, 51.7, 54.9, 56.4, 59.5, 62.0, 65.3), +) +PART_D_DECEMBER_31_CREDIT = {2015: 0.2, 2020: 0.1, 2026: 0.2} +# Table IV.B10 (page 159), low-income subsidy, $ billions; 2015 from the +# 2025 report's Table IV.B10 (page 159). +LOW_INCOME_SUBSIDY = _cells( + *(25.6, 26.4, 27.3, 28.5, 29.7, 33.0, 35.0, 39.4, 44.2, 41.2, 17.4), + *(18.1, 20.5, 22.1, 24.5, 28.9, 30.7, 31.7, 32.4, 33.3, 34.8), +) +# Table V.E4 (page 218), Part D income-related premiums, $ billions. +PART_D_IRMAA = _cells( + *(0.9, 1.0, 1.2, 1.4, 1.6, 1.8, 1.8, 1.7, 2.2, 2.2, 2.4, 3.1, 3.5), + *(4.0, 4.6, 10.8, 11.9, 12.6, 13.7, 14.7, 15.9), +) +# 2019: Table III.C2 (page 89) standard monthly premium, Table V.B3 Part B +# enrollment (thousands), Table V.E3 (page 215) income-related premiums +# ($ billions). +STANDARD_PART_B_PREMIUM_2019 = 135.50 +PART_B_ENROLLMENT_2019 = 56_019 +PART_B_IRMAA_2019 = 8.4 + + +def _part_b_billions(year): + if year in PART_B_INCURRED: + return PART_B_INCURRED[year] / 1e3 + # III.C4 footnote 7: January 2027 withholdings credited December 31, 2026. + december_31_credit = PART_B_CASH[2026] - PART_B_INCURRED[2026] / 1e3 + return PART_B_CASH[year] + (december_31_credit if year == 2027 else 0) + + +def _part_d_billions(year): + return ( + PART_D_CASH[year] + - PART_D_DECEMBER_31_CREDIT.get(year, 0) + + PART_D_DECEMBER_31_CREDIT.get(year - 1, 0) + ) + + +AGGREGATES = { + "part_a": PART_A_PREMIUMS.get, + "part_b": _part_b_billions, + "part_d": _part_d_billions, + "lis": LOW_INCOME_SUBSIDY.get, +} + + +def _read(name, date): + return get_parameter(system.parameters, name)(date) + + +def _value(series, year): + return _read(SERIES[series], f"{year}-01-01") + + +def _spending(year): + return _read(SPENDING, f"{year}-01-01") + + +def _per_enrollee(billions, year): + return billions * 1e9 / (TOTAL_ENROLLMENT[year] * 1e3) + + +@pytest.mark.parametrize("series", SERIES) +@pytest.mark.parametrize("year", YEARS) +def test_series_is_the_trustees_aggregate_per_medicare_enrollee(series, year): + expected = _per_enrollee(AGGREGATES[series](year), year) + assert _value(series, year) == round(expected, 2) + + +@pytest.mark.parametrize("series", SERIES) +@pytest.mark.parametrize("year", YEARS) +def test_series_times_enrollment_returns_the_aggregate(series, year): + rebuilt = _value(series, year) * TOTAL_ENROLLMENT[year] * 1e3 / 1e9 + # Stored to the cent; enrollment of at most 81 million moves the + # aggregate by at most $0.0005 billion. + assert rebuilt == pytest.approx(AGGREGATES[series](year), abs=1e-3) + + +@pytest.mark.parametrize("year", YEARS) +def test_part_d_series_matches_share_times_premium_plus_irmaa(year): + share = _read(f"{MEDICARE}.part_d.enrollment_share", f"{year}-01-01") + premium = _read(f"{MEDICARE}.part_d.premiums_per_enrollee", f"{year}-01-01") + irmaa = _per_enrollee(PART_D_IRMAA[year], year) + # The share is stored to four decimals and the premium to the cent, so + # the product can be off by up to about $0.05. + assert share * premium + irmaa == pytest.approx(_value("part_d", year), abs=0.1) + + +def test_part_b_premium_income_includes_income_related_premiums(): + standard = STANDARD_PART_B_PREMIUM_2019 * 12 * PART_B_ENROLLMENT_2019 * 1e3 / 1e9 + published = _part_b_billions(2019) + assert standard + PART_B_IRMAA_2019 == pytest.approx(published, rel=0.005) + assert standard < published * 0.95 + + +@given(st.integers(FIRST_YEAR, END_YEAR + 100)) +def test_series_are_positive_and_net_spending_is_between_zero_and_gross(year): + deductions = sum(_value(series, year) for series in SERIES) + assert all(_value(series, year) > 0 for series in SERIES) + assert 0 < _spending(year) - deductions < _spending(year) + + +@pytest.mark.parametrize("series", SERIES) +def test_extension_keeps_the_2035_share_of_spending(series): + share_2035 = _value(series, LAST_PROJECTED_YEAR) / _spending(LAST_PROJECTED_YEAR) + for year in range(LAST_PROJECTED_YEAR + 1, END_YEAR + 1): + assert _value(series, year) / _spending(year) == pytest.approx( + share_2035, rel=1e-9 + ) + + +@given(st.integers(END_YEAR, END_YEAR + 100)) +def test_series_hold_the_2100_values_afterwards(year): + for series in SERIES: + assert _value(series, year) == _value(series, END_YEAR) + + +@given(st.integers(FIRST_YEAR, END_YEAR), st.integers(1, 12), st.integers(1, 28)) +def test_every_date_in_a_year_reads_the_january_value(year, month, day): + date = f"{year}-{month:02d}-{day:02d}" + for series in SERIES: + assert _read(SERIES[series], date) == _value(series, year) diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/medicare/costs/cbo_medicare_benefit.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/costs/cbo_medicare_benefit.yaml new file mode 100644 index 00000000000..f3e1652a12b --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/hhs/medicare/costs/cbo_medicare_benefit.yaml @@ -0,0 +1,111 @@ +# cbo_medicare_benefit = Medicare spending per enrollee - premium income per +# Medicare enrollee (Parts A, B and D) - Part D low-income subsidy per +# Medicare enrollee, the same for every enrollee. +# 2025: $17,751 - $82.26 - $2,169.44 - $215.04 - $251.12 = $15,033.14. + +- name: Case 1, enrolled person gets the average net of premiums and the low-income subsidy. + period: 2025 + absolute_error_margin: 0.01 + input: + age: 70 + is_medicare_eligible: true + medicare_enrolled: true + output: + medicare_gross_cost: 17_751 + cbo_medicare_benefit: 15_033.14 + +# CBO assigns the average to every enrollee, so the person with 20 quarters +# of coverage (a $6,216 Part A premium in 2025) and the person paying the +# income-related premiums get the same value as the others, though each one's +# medicare_cost differs. +- name: Case 2, every enrollee gets the same value whatever premiums they pay. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + premium_free_part_a: + age: 70 + is_medicare_eligible: true + medicare_enrolled: true + medicare_quarters_of_coverage: 40 + pays_part_a_premium: + age: 70 + is_medicare_eligible: true + medicare_enrolled: true + medicare_quarters_of_coverage: 20 + pays_irmaa: + age: 70 + is_medicare_eligible: true + medicare_enrolled: true + medicare_quarters_of_coverage: 40 + disabled_under_65: + age: 50 + is_medicare_eligible: true + medicare_enrolled: true + medicare_quarters_of_coverage: 40 + not_enrolled: + age: 70 + is_medicare_eligible: true + medicare_enrolled: false + tax_units: + tax_unit: + members: [premium_free_part_a] + adjusted_gross_income: + 2023: 30_000 + tax_unit_2: + members: [pays_part_a_premium] + adjusted_gross_income: + 2023: 30_000 + tax_unit_3: + members: [pays_irmaa] + # IRMAA uses income from two years before. + adjusted_gross_income: + 2023: 1_000_000 + tax_unit_4: + members: [disabled_under_65] + tax_unit_5: + members: [not_enrolled] + households: + household: + members: + [ + premium_free_part_a, + pays_part_a_premium, + pays_irmaa, + disabled_under_65, + not_enrolled, + ] + output: + cbo_medicare_benefit: [15_033.14, 15_033.14, 15_033.14, 15_033.14, 0] + +- name: Case 3, first year of the series. + period: 2015 + absolute_error_margin: 0.01 + input: + age: 70 + is_medicare_eligible: true + medicare_enrolled: true + output: + # $11,929 - $57.57 - $1,214.54 - $224.86 - $460.52. + cbo_medicare_benefit: 9_971.51 + +- name: Case 4, last Trustees projection year. + period: 2035 + absolute_error_margin: 0.01 + input: + age: 70 + is_medicare_eligible: true + medicare_enrolled: true + output: + # $31,764.35 (the 2034 CMS value grown at the 2031-2032 rate) - $156.27 + # - $4,652 - $809.85 - $431.59. + cbo_medicare_benefit: 25_714.64 + +- name: Case 5, not enrolled. + period: 2025 + input: + age: 70 + is_medicare_eligible: true + medicare_enrolled: false + output: + cbo_medicare_benefit: 0 diff --git a/policyengine_us/tests/policy/baseline/household/income/household/cbo_household_income.yaml b/policyengine_us/tests/policy/baseline/household/income/household/cbo_household_income.yaml index 07d9f4f1d8e..d52fdf17f34 100644 --- a/policyengine_us/tests/policy/baseline/household/income/household/cbo_household_income.yaml +++ b/policyengine_us/tests/policy/baseline/household/income/household/cbo_household_income.yaml @@ -14,6 +14,7 @@ - name: CBO before-tax income adds social insurance and alias matches period: 2025 + absolute_error_margin: 0.01 input: employment_income: 40_000 employer_sponsored_insurance_premiums: 6_000 @@ -35,10 +36,14 @@ cbo_household_market_income: 49_102 # CMS Medicare spending per enrollee, 2025. medicare_gross_cost: 17_751 - # $12,000 + $17,751 + $2,000 + $1,000. - cbo_household_social_insurance_benefits: 32_751 - cbo_household_income_before_transfers_and_taxes: 81_853 - cbo_household_income: 81_853 + # $17,751 less 2025 premium income per Medicare enrollee ($82.26 Part A, + # $2,169.44 Part B, $215.04 Part D) and the $251.12 Part D low-income + # subsidy per Medicare enrollee. + cbo_medicare_benefit: 15_033.14 + # $12,000 + $15,033.14 + $2,000 + $1,000. + cbo_household_social_insurance_benefits: 30_033.14 + cbo_household_income_before_transfers_and_taxes: 79_135.14 + cbo_household_income: 79_135.14 - name: CBO means-tested transfers include only the listed programs period: 2026 diff --git a/policyengine_us/variables/gov/hhs/medicare/costs/cbo_medicare_benefit.py b/policyengine_us/variables/gov/hhs/medicare/costs/cbo_medicare_benefit.py new file mode 100644 index 00000000000..1a4e1d96df5 --- /dev/null +++ b/policyengine_us/variables/gov/hhs/medicare/costs/cbo_medicare_benefit.py @@ -0,0 +1,36 @@ +from policyengine_us.model_api import * + + +class cbo_medicare_benefit(Variable): + value_type = float + entity = Person + label = "CBO Medicare benefit" + documentation = ( + "Medicare benefit as the Congressional Budget Office counts it in " + "household income: the government's average cost per enrollee of " + "providing Medicare Parts A, B and D, net of premiums paid by or on " + "behalf of enrollees. CBO assigns the same average to every " + "enrollee and counts the Part D low-income subsidy separately as a " + "means-tested transfer, so this subtracts per-enrollee averages of " + "premium income and of the low-income subsidy from Medicare " + "spending per enrollee, rather than the person's own premiums." + ) + unit = USD + definition_period = YEAR + reference = ( + "https://www.cbo.gov/publication/61910", + "https://www.cbo.gov/system/files/2024-09/60341-income.pdf#page=27", + "https://www.cbo.gov/system/files/2018-07/54234-workingpaper.pdf#page=46", + ) + defined_for = "medicare_enrolled" + + def formula(person, period, parameters): + p = parameters(period).calibration.gov.hhs.medicare + premiums = p.premium_income_per_enrollee + total_premiums = premiums.part_a + premiums.part_b + premiums.part_d + net = ( + p.per_capita_cost + - total_premiums + - p.part_d_low_income_subsidy_per_enrollee + ) + return max_(net, 0) diff --git a/policyengine_us/variables/gov/hhs/medicare/costs/medicare_gross_cost.py b/policyengine_us/variables/gov/hhs/medicare/costs/medicare_gross_cost.py index 48533e92fb4..1baeeaf188a 100644 --- a/policyengine_us/variables/gov/hhs/medicare/costs/medicare_gross_cost.py +++ b/policyengine_us/variables/gov/hhs/medicare/costs/medicare_gross_cost.py @@ -7,12 +7,19 @@ class medicare_gross_cost(Variable): label = "Gross Medicare cost" documentation = ( "Annual Medicare spending on behalf of the beneficiary before " - "subtracting beneficiary premiums. This matches the gross Medicare " - "benefit concept used in the CBO household-income framework." + "subtracting beneficiary premiums: CMS Medicare spending per " + "enrollee, which covers Parts A through D, including the Part D " + "low-income subsidy. CBO's standard household income counts " + "Medicare net of premiums instead (cbo_medicare_benefit); CBO adds " + "the premiums back only in an expanded sensitivity measure, which " + "it calls gross Medicare spending." ) unit = USD definition_period = YEAR - reference = "https://www.cms.gov/about-cms/what-we-do/medicare" + reference = ( + "https://www.cms.gov/about-cms/what-we-do/medicare", + "https://www.cbo.gov/publication/61910", + ) defined_for = "medicare_enrolled" def formula(person, period, parameters): diff --git a/policyengine_us/variables/household/income/household/cbo_household_social_insurance_benefits.py b/policyengine_us/variables/household/income/household/cbo_household_social_insurance_benefits.py index b39c09e5db5..82ecc087d38 100644 --- a/policyengine_us/variables/household/income/household/cbo_household_social_insurance_benefits.py +++ b/policyengine_us/variables/household/income/household/cbo_household_social_insurance_benefits.py @@ -7,8 +7,8 @@ class cbo_household_social_insurance_benefits(Variable): label = "CBO household social insurance benefits" documentation = ( "Social insurance benefits included in CBO household income: Social " - "Security, gross Medicare benefits, unemployment insurance, and " - "workers' compensation." + "Security, Medicare benefits net of enrollee premiums, unemployment " + "insurance, and workers' compensation." ) definition_period = YEAR unit = USD