Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/medicare-noninstitutional-spending.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Scale Medicare spending per enrollee to enrollees who do not live in nursing homes in medicare_gross_cost, medicare_cost, cbo_medicare_benefit and qmb_cost_sharing, since the survey data exclude nursing home residents.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
description: PolicyEngine multiplies Medicare spending per enrollee by this ratio of Medicare spending per enrollee who is not a long-stay nursing home resident to Medicare spending per enrollee, because the household survey data exclude people living in nursing homes.
metadata:
unit: /1
period: year
label: Medicare noninstitutionalized spending ratio
reference:
# Medicare spending per enrollee (per_capita_cost) averages over every
# enrollee, including nursing home residents, whom the Current Population
# Survey does not sample. In 2022, among beneficiaries in fee-for-service
# Medicare, annual Part A and Part B program spending averaged $31,200 for
# long-stay nursing home residents and about $11,300 for other
# beneficiaries (June 2025, file page 9), and long-stay residents were 1.7
# percent of beneficiaries (October 2024, file page 5; 1.7 percent again in
# 2023, June 2025, file page 8). A long-stay resident had been in a nursing
# home for more than 90 days in at least one month of the year. So spending
# per other beneficiary relative to spending per beneficiary is
# 1 / (1 + 0.017 * (31_200 / 11_300 - 1)) = 0.971. The preliminary October
# 2024 slide described long-stay residents' spending as about two times
# higher; the June 2025 report's dollar figures, almost three times as
# high, replace that description.
#
# The ratio is measured for fee-for-service Parts A and B and applied to
# all Medicare spending per enrollee, including Medicare Advantage, Part D
# and administration. With fee-for-service enrollment shares instead (62
# percent of long-stay residents and 53 percent of others in 2023, June
# 2025, file page 13) it is 0.966; pricing Medicare Advantage at the
# long-stay residents' risk score ratio (2.12 / 1.01, October 2024, file
# page 5) gives about 0.975. The long-stay share was 2.6 percent in 2013
# (June 2025, file page 8), but MedPAC publishes relative spending for 2022
# only, so the 2022 ratio applies to every year from 2013, the first year of
# per_capita_cost.
- title: "Medicare Payment Advisory Commission, Report to the Congress, June 2025, Chapter 5, Medicare beneficiaries in nursing homes #page 8-9,13"
href: https://www.medpac.gov/wp-content/uploads/2025/06/Jun25_Ch5_MedPAC_Report_To_Congress_SEC.pdf#page=8
- title: Medicare Payment Advisory Commission, Medicare beneficiaries in nursing homes, October 2024 public meeting
href: https://www.medpac.gov/wp-content/uploads/2023/10/Beneficiaries-in-Nursing-Homes-MedPAC-Oct-2024-SEC.pdf#page=5
# The Current Population Survey samples housing units and some
# noninstitutional group quarters; institutional group quarters such as
# nursing homes have never been part of its sample. The census counts
# nursing facility patients at the facility, but counts residents of
# assisted living facilities and of housing for older adults, such as
# retirement communities, at the residence where they live and sleep most
# of the time (sections C.11(c), C.11(d) and C.12).
- title: U.S. Bureau of Labor Statistics, Handbook of Methods, Current Population Survey, Design
href: https://www.bls.gov/opub/hom/cps/design.htm
- title: "U.S. Census Bureau, Final 2020 Census Residence Criteria and Residence Situations, 83 FR 5525 #page 10-11"
href: https://www.govinfo.gov/content/pkg/FR-2018-02-08/pdf/2018-02370.pdf#page=10
# The Medicare Current Beneficiary Survey's long-term care facilities
# include retirement homes, personal care facilities and assisted and
# foster care homes as well as nursing homes (Appendix B). Excluding
# everyone living in one at the fall interview gives a larger gap: in
# 2013, community residents' Medicare benefit payments per capita were
# 0.92 of the average over all beneficiaries (Table 4.1 over Table 1.1).
- title: Centers for Medicare and Medicaid Services, Characteristics and Perceptions of the Medicare Population, 2013, Tables 1.1 and 4.1 and Appendix B
href: https://www.cms.gov/research-statistics-data-and-systems/research/mcbs/downloads/2013_characteristics_and_perceptions.zip
# CBO also removes institutionalized enrollees' benefits before assigning
# Medicare's average cost to survey respondents (file page 46). It holds
# the institutional share of Medicare benefits other than the low-income
# subsidy at its 2012 level from 2013 (file page 52), where this parameter
# instead holds the ratio of spending per person constant.
- title: "Congressional Budget Office, Working Paper 2018-07, How CBO Adjusts for Survey Underreporting of Transfer Income in Its Distributional Analyses #page 46,52"
href: https://www.cbo.gov/system/files/2018-07/54234-workingpaper.pdf#page=46
values:
2013-01-01: 0.971
24 changes: 15 additions & 9 deletions policyengine_us/tests/core/test_cbo_medicare_benefit_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
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 Medicare Trustees Reports over Table V.B3's total
enrollment (people with Part A and/or Part B). From 2015 they come from the
2026 report:
spending per enrollee (``calibration.gov.hhs.medicare.per_capita_cost``) less
the low-income subsidy, scales it to enrollees outside nursing homes
(``noninstitutionalized_spending_ratio``), and subtracts premium income.
The four series under ``calibration.gov.hhs.medicare`` are each an aggregate
from the Medicare Trustees Reports over Table V.B3's total enrollment (people
with Part A and/or Part B). From 2015 they come from the 2026 report:

- ``premium_income_per_enrollee.part_a``: Table III.B4 premiums from
voluntary enrollees.
Expand Down Expand Up @@ -40,8 +41,10 @@
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 2013 to 2200.
5. Bounds: every series is positive, and net Medicare spending per enrollee,
(spending - low-income subsidy) x the noninstitutionalized ratio -
premiums, stays between 0 and scaled spending, in every year from 2013 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.
Expand All @@ -63,6 +66,7 @@
"lis": f"{MEDICARE}.part_d_low_income_subsidy_per_enrollee",
}
SPENDING = f"{MEDICARE}.per_capita_cost"
RATIO = f"{MEDICARE}.noninstitutionalized_spending_ratio"
FIRST_YEAR, LAST_PROJECTED_YEAR, END_YEAR = 2015, 2035, 2100
YEARS = range(FIRST_YEAR, LAST_PROJECTED_YEAR + 1)

Expand Down Expand Up @@ -225,9 +229,11 @@ def test_part_b_premium_income_includes_income_related_premiums():
@given(st.integers(BACKFILL_YEARS[0], END_YEAR + 100))
@example(BACKFILL_YEARS[0])
def test_series_are_positive_and_net_spending_is_between_zero_and_gross(year):
deductions = sum(_value(series, year) for series in SERIES)
ratio = _read(RATIO, f"{year}-01-01")
premiums = sum(_value(series, year) for series in SERIES if series != "lis")
net = (_spending(year) - _value("lis", year)) * ratio - premiums
assert all(_value(series, year) > 0 for series in SERIES)
assert 0 < _spending(year) - deductions < _spending(year)
assert 0 < net < _spending(year) * ratio


@pytest.mark.parametrize("series", SERIES)
Expand Down
17 changes: 11 additions & 6 deletions policyengine_us/tests/core/test_medicare_cost_part_d_premium.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""The Part D premium that ``medicare_cost`` subtracts.

CMS Medicare spending per enrollee (``calibration.gov.hhs.medicare.
per_capita_cost``) covers Parts A through D, averaged over every Medicare
enrollee, gross of the premiums that finance it. ``medicare_cost`` subtracts
the Part D premium averaged the same way, plus the person's own
income-related monthly adjustment amount (IRMAA):
per_capita_cost``, scaled by ``noninstitutionalized_spending_ratio`` to
enrollees outside nursing homes) covers Parts A through D, with Part D
averaged over every Medicare enrollee, gross of the premiums that finance it.
``medicare_cost`` subtracts the Part D premium averaged the same way, plus the
person's own income-related monthly adjustment amount (IRMAA):

enrollment_share x premiums_per_enrollee + Part D IRMAA

Expand Down Expand Up @@ -38,7 +39,8 @@
5. Calendar years: every date in a year reads the January 1 value; between
the Trustees' five-year enrollment points the share holds the earlier one.
6. Consumer (Hypothesis, differential): ``medicare_cost`` equals a reference
implementation, max(gross - Part A - Part B - share x premium - IRMAA, 0),
implementation, max(gross - Part A - Part B - share x premium - IRMAA, 0)
with gross = per_capita_cost x ``noninstitutionalized_spending_ratio``,
for any year and premiums; it lies in [0, ``medicare_gross_cost``], which
does not change with the Part D premium.
7. Accounting (Hypothesis): the person's Part D IRMAA lowers
Expand Down Expand Up @@ -232,7 +234,10 @@ def _enrollee(year, part_a, part_b, irmaa):


def _reference_medicare_cost(year, part_a, part_b, irmaa):
gross = _read("calibration.gov.hhs.medicare.per_capita_cost", f"{year}-01-01")
date = f"{year}-01-01"
gross = _read("calibration.gov.hhs.medicare.per_capita_cost", date) * _read(
"calibration.gov.hhs.medicare.noninstitutionalized_spending_ratio", date
)
part_d = _share(year) * _premium(year) + irmaa
return max(gross - part_a - part_b - part_d, 0)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
"""The ratio that scales Medicare spending per enrollee to enrollees outside
nursing homes.

``calibration.gov.hhs.medicare.per_capita_cost`` averages Medicare spending
over every enrollee, including nursing home residents, whom the household
survey data do not sample. ``noninstitutionalized_spending_ratio`` is
spending per enrollee who is not a long-stay nursing home resident over
spending per enrollee, from MedPAC's 2022 fee-for-service Part A and B
spending ($31,200 for long-stay residents, about $11,300 for others; June
2025 report, chapter 5, file page 9) and the long-stay share of beneficiaries
(1.7 percent; October 2024 slides, file page 5):

ratio = 1 / (1 + share x (long-stay spending / other spending - 1))

These tests read parameters only. Tests elsewhere cover the consumers at the
stored ratio: the YAML tests of ``medicare_gross_cost``, ``medicare_cost``,
``cbo_medicare_benefit`` and ``qmb_cost_sharing``, and the reference
implementations in test_medicare_per_capita_cost.py,
test_medicare_cost_part_d_premium.py and test_cbo_medicare_benefit_series.py,
which multiply by this parameter.

Invariants:

1. Transcription: the stored value is the formula applied to MedPAC's
published figures, rounded to three decimals.
2. Bounds and constancy: the ratio is strictly between 0 and 1 and is the
same on every date from 2013, the first year of ``per_capita_cost``.
3. Sensitivity: the alternatives documented in the parameter file bracket
the stored value: fee-for-service enrollment shares (62 percent of
long-stay residents, 53 percent of others) give a lower ratio, and pricing
Medicare Advantage at the CMS-HCC risk score ratio (2.12 / 1.01) gives a
higher one.
4. Definition order: excluding everyone living in a Medicare Current
Beneficiary Survey long-term care facility, which includes assisted living
and retirement homes, gives a lower ratio in every year of the published
2006-2013 tables (community-only over all-beneficiary benefit payments per
capita), so the nursing home ratio is the smaller of the two corrections.
5. Differential: CBO's 2006 figures, from a different method and source,
imply a ratio per recipient and per survey reporter within 0.01 of the
stored 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

RATIO = "calibration.gov.hhs.medicare.noninstitutionalized_spending_ratio"
FIRST_YEAR, END_YEAR = 2013, 2100

# MedPAC, June 2025 Report to the Congress, chapter 5 (file pages 8, 9 and
# 13), and October 2024 meeting slides (file page 5).
LONG_STAY_SPENDING, OTHER_SPENDING = 31_200, 11_300
LONG_STAY_SHARE = 0.017
LONG_STAY_FFS_SHARE, OTHER_FFS_SHARE = 0.62, 0.53
LONG_STAY_RISK_SCORE, OTHER_RISK_SCORE = 2.12, 1.01

# MCBS Characteristics and Perceptions of the Medicare Population, each
# year's edition
# (https://www.cms.gov/research-statistics-data-and-systems/research/mcbs/
# downloads/<year>_characteristics_and_perceptions.zip), all beneficiaries:
# Table 1.1 beneficiaries (thousands) and Table 4.1 benefit payments ($
# millions), total and community only.
MCBS = {
2006: (43_867, 41_599, 305_066, 267_344),
2007: (45_046, 42_867, 307_372, 271_509),
2008: (46_100, 43_906, 320_032, 282_887),
2009: (47_371, 45_053, 335_081, 289_843),
2010: (48_454, 46_270, 365_098, 322_150),
2011: (50_186, 47_969, 374_252, 330_907),
2012: (51_723, 49_816, 338_395, 299_144),
2013: (53_574, 51_568, 352_943, 312_626),
}

# CBO 2006, in billions of dollars. Working Paper 2018-07: 93 percent of
# enrollees reported Medicare in the survey and 6 percent of recipients were
# institutionalized (file pages 45-46); Medicare benefits other than the
# low-income subsidy after CBO's adjustments, $352.4, and the subsidy, $10.3
# (file page 51). CBO (2013), The Distribution of Federal Spending and Taxes
# in 2006: $32 of Medicare spending for people in long-term care institutions
# (file page 53). 2007 Medicare Trustees Report, Table II.B1: 2006 benefits,
# $402.0, and Part A premiums, $2.6, which CBO subtracts (file page 52).
CBO_REPORTING_RATE, CBO_INSTITUTIONALIZED_SHARE = 0.93, 0.06
CBO_ADJUSTED_BENEFITS, CBO_LOW_INCOME_SUBSIDY = 352.4, 10.3
CBO_INSTITUTIONAL_SPENDING = 32
TRUSTEES_BENEFITS, TRUSTEES_PART_A_PREMIUMS = 402.0, 2.6


def _ratio(share, relative):
return 1 / (1 + share * (relative - 1))


def _stored(date):
return get_parameter(system.parameters, RATIO)(date)


def test_value_is_the_medpac_formula():
expected = _ratio(LONG_STAY_SHARE, LONG_STAY_SPENDING / OTHER_SPENDING)
assert _stored("2022-01-01") == round(expected, 3) == 0.971


@given(st.integers(FIRST_YEAR, END_YEAR + 100), st.integers(1, 12))
def test_ratio_is_a_constant_share_below_one(year, month):
value = _stored(f"{year}-{month:02d}-01")
assert 0 < value < 1
assert value == _stored("2022-01-01")


def test_documented_alternatives_bracket_the_value():
relative = LONG_STAY_SPENDING / OTHER_SPENDING
ffs_share = (
LONG_STAY_SHARE
* LONG_STAY_FFS_SHARE
/ (
LONG_STAY_SHARE * LONG_STAY_FFS_SHARE
+ (1 - LONG_STAY_SHARE) * OTHER_FFS_SHARE
)
)
ffs_ratio = _ratio(ffs_share, relative)
# Price Medicare Advantage enrollees at the risk score ratio, with other
# beneficiaries' spending the same in both programs.
groups = {
"long_stay_ffs": (LONG_STAY_SHARE * LONG_STAY_FFS_SHARE, relative),
"long_stay_ma": (
LONG_STAY_SHARE * (1 - LONG_STAY_FFS_SHARE),
LONG_STAY_RISK_SCORE / OTHER_RISK_SCORE,
),
"other": (1 - LONG_STAY_SHARE, 1),
}
average = sum(count * cost for count, cost in groups.values())
mixed_ratio = 1 / average
assert round(ffs_ratio, 3) == 0.966
assert round(mixed_ratio, 3) == 0.975
assert ffs_ratio < _stored("2022-01-01") < mixed_ratio


@pytest.mark.parametrize("year", MCBS)
def test_mcbs_facility_definition_gives_a_larger_gap(year):
total_people, community_people, total_pay, community_pay = MCBS[year]
mcbs_ratio = (community_pay / community_people) / (total_pay / total_people)
# The stored ratio starts in 2013, so compare every year to its value.
assert 0.9 < mcbs_ratio < _stored("2022-01-01")


def test_cbo_2006_figures_imply_a_similar_ratio():
benefits = TRUSTEES_BENEFITS - TRUSTEES_PART_A_PREMIUMS - CBO_LOW_INCOME_SUBSIDY
assert benefits == pytest.approx(389.1)
per_recipient = (1 - CBO_INSTITUTIONAL_SPENDING / benefits) / (
1 - CBO_INSTITUTIONALIZED_SHARE
)
per_reporter = CBO_ADJUSTED_BENEFITS / benefits / CBO_REPORTING_RATE
assert round(per_recipient, 3) == 0.976
assert round(per_reporter, 3) == 0.974
for implied in (per_recipient, per_reporter):
assert implied == pytest.approx(_stored("2013-01-01"), abs=0.01)
Loading