Skip to content

Add ACA marketplace plan selection proxies#618

Open
daphnehanse11 wants to merge 4 commits intomainfrom
codex/aca-marketplace-plan-selection
Open

Add ACA marketplace plan selection proxies#618
daphnehanse11 wants to merge 4 commits intomainfrom
codex/aca-marketplace-plan-selection

Conversation

@daphnehanse11
Copy link
Collaborator

Summary

This PR adds ACA marketplace plan-selection proxies to the calibration pipeline.

It introduces a simple bronze-vs-benchmark selection layer that can be used to model households leaving part of the benchmark-based ACA credit unused when they choose a cheaper bronze plan.

Concretely, this PR:

  • adds CMS-derived 2024 marketplace proxy tables under policyengine_us_data/storage/calibration_targets
  • adds a shared marketplace_plan_selection helper for seeded bronze-vs-benchmark assignment
  • wires those assignments into publish_local_area.py
  • wires derived proxy aggregates into unified_matrix_builder.py
  • adds tests for the proxy builder and seeded assignment logic

New derived outputs

This PR adds calibration-side / published-data support for:

  • selected_marketplace_plan_premium_proxy
  • used_aca_ptc
  • unused_aca_ptc

Published tax-unit inputs now also include:

  • selects_bronze_marketplace_plan
  • selected_marketplace_plan_benchmark_ratio
  • state_marketplace_bronze_probability
  • state_marketplace_bronze_to_benchmark_ratio

Data sources and fallbacks

The proxy builder uses 2024 CMS marketplace public use files where available.

For missing SBM price menus, the derived state ratio table carries an explicit state-specific fallback with provenance columns (source, source_year, source_basis) so the fallback is visible rather than hidden in code.

Validation

Automated tests:

  • pytest policyengine_us_data/tests/test_marketplace_plan_selection.py policyengine_us_data/tests/test_aca_marketplace_plan_selection_proxies.py -q
  • pytest policyengine_us_data/tests/test_calibration/test_unified_matrix_builder.py -q

Smoke test:

  • built an SC-only synthetic target matrix for selected_marketplace_plan_premium_proxy, used_aca_ptc, and unused_aca_ptc
  • published an H5 from the same geography and unit weights
  • recomputed the same totals from the published H5
  • matrix totals and H5 recomputation matched to float noise
  • used_aca_ptc + unused_aca_ptc == aca_ptc held exactly in the published H5 audit

Notes

This PR is additive plumbing. It does not:

  • change ACA law logic in policyengine-us
  • change the current calibration target set
  • make these variables part of the optimization objective yet

A follow-up rules PR in policyengine-us can expose the new tax-unit variables as first-class model variables.

Copy link
Collaborator

@baogorek baogorek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @daphnehanse11 , I'm going to let my Claude do the talking below, but the short of it is that there's a lot to do. I think Codex went for the quick win, and there's just not a quick win here.

  the CMS data sourcing is thorough and the underlying goal of decomposing PTC into used vs. unused makes sense. However, I think
   the approach needs to be restructured. The matrix builder should stay generic and not contain variable-specific logic, and the variables you're deriving   
  don't yet exist in the places they need to for calibration to actually work.                                                                             
                                                                                                                                                              
  Here's the full path I'd suggest, roughly in dependency order:      
                                                                                                                                                              
  1. policyengine-us: Add used_aca_ptc, unused_aca_ptc, and selects_bronze_marketplace_plan as real calculated variables with formulas and parameters. The    
  state-level bronze selection probabilities and price ratios from your CMS data become parameters there. Everything downstream depends on these existing     
  first.                                                                                                                                                      
  2. ETL scripts (policy_data.db): Derive state-level calibration targets (e.g., total used PTC by state) from the CMS data and load them into the targets
  database. That's where calibration targets live now.                                                                                                        
  3. enhanced_cps.py: Wire up the bronze plan selection so the legacy calibration pipeline has access to the new variables.
  4. target_config.yaml: Add the new variable names so the unified matrix builder picks them up — no code changes to the builder itself, just config.         
                                                                                                                                                              
  With this approach, the matrix builder never needs to know what these variables are. It just sees new names in the config and new rows in the database, same
   as any other target.                                                                                                                                       
                                                                                                                                                              
  I'd suggest starting with step 1 since everything else depends on it.               
                                                                     

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants