PolicyEngine / PolicyEngine/policyengine-us

MOOP should use computed premiums instead of fully imputed, so reforms propagate to SPM

Open
#8,089 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
162
Forks
212
Avg merge
3d 3h
Merged PRs (30d)
97

Description

Follow-up from #8086 and #8088.

## Problem

Medical out-of-pocket expenses (MOOP) are used to compute SPM resources:

```
spm_unit_net_income = market_income + benefits − taxes − spm_expenses
spm_expenses = child_support + medical_out_of_pocket_expenses + capped_childcare
medical_out_of_pocket_expenses = health_insurance_premiums + other_medical_expenses
```

`health_insurance_premiums` (and its Medicare-Part-B split) are **pure CPS-imputed inputs** — no formula, just survey data uprated via `calibration.gov.hhs.cms.moop_per_capita`:

- `policyengine_us/variables/household/expense/health/health_insurance_premiums_without_medicare_part_b.py`
- `policyengine_us/variables/household/expense/health/medicare_part_b_premiums.py`

This means:
- **Baseline** SPM is fine: imputed MOOP already includes whatever CHIP / Medicare / Marketplace premiums CPS respondents reported.
- **Reforms** break: if a reform eliminates CHIP premiums, simulated `chip_premium` drops to zero, but MOOP (survey-imputed) doesn't change, so SPM resources don't respond. Same for Medicare Part B IRMAA reforms, Marketplace benchmark changes, etc.

## Proposed architecture

Whatever we compute from policy rules should *replace* (not add to) the corresponding slice of imputed MOOP:

```
medical_out_of_pocket_expenses = imputed_moop_residual
+ computed_chip_premium
+ computed_medicare_part_b_premium
+ computed_marketplace_net_premium
+ other_computed_premiums...
```

where

```
imputed_moop_residual = imputed_moop_total - baseline_computed_premium_sum
```

`baseline_computed_premium_sum` is pre-computed per CPS record during data construction using the rules that were in effect during the CPS reference year (currently 2024). In baseline simulations the two cancel; in reforms the computed piece moves and SPM adjusts correctly.

## What's computable today

| Program | Can we compute it from rules? | Status |
|---------|------------------------------|--------|
| CHIP premiums | Yes — #8086 | `chip_premium` (17 states) |
| Medicare Part B | Yes — statutory base + IRMAA tiers by MAGI | Not computed |
| Marketplace net premium | Yes — silver benchmark × applicable-fig percent of income − PTC | `premium_tax_credit` computed; net premium not assembled |
| Medicaid cost-sharing | Partial — varies widely by state | Out of scope |
| Copays / deductibles | No — not rule-driven | Stay in imputed residual |

## Staging

1. **us-model (this issue / follow-up PR)** — add `chip_premium` to the MOOP chain so reforms to CHIP premiums propagate to SPM. Accepts a small baseline double-count until step 2 lands.
2. **us-data** — during data construction, compute each CPS record's CHIP premium under 2024 rules (the reference year) and subtract it from the imputed `health_insurance_premiums_without_medicare_part_b` component. After this, baseline reconciles exactly.
3. **us-model (later)** — rules-based Medicare Part B (standard premium + IRMAA by MAGI tier), plug into same MOOP chain.
4. **us-data (later)** — strip Medicare Part B from imputed MOOP using 2024 rules.
5. **us-model / us-data (later)** — rules-based Marketplace net premium using existing `premium_tax_credit` plus benchmark × affordability percent.

## Notes

- The "2024 rules" requirement for step 2 comes from the CPS reference year. As the dataset updates (CPS ASEC 2026 → reference year 2025 etc.), the anchor year updates too.
- Entity-mismatch caveat: `chip_premium` is TaxUnit-level, `medical_out_of_pocket_expenses` is Person-level. Aggregation goes through `add()` at the SPM unit level (existing precedent: `spm_unit_benefits` adds TaxUnit-level `premium_tax_credit`). Architecturally clean at the SPM-unit boundary.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.