Add ACA marketplace plan selection proxies#618
Open
daphnehanse11 wants to merge 4 commits intomainfrom
Open
Conversation
baogorek
requested changes
Mar 18, 2026
Collaborator
baogorek
left a comment
There was a problem hiding this comment.
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
policyengine_us_data/storage/calibration_targetsmarketplace_plan_selectionhelper for seeded bronze-vs-benchmark assignmentpublish_local_area.pyunified_matrix_builder.pyNew derived outputs
This PR adds calibration-side / published-data support for:
selected_marketplace_plan_premium_proxyused_aca_ptcunused_aca_ptcPublished tax-unit inputs now also include:
selects_bronze_marketplace_planselected_marketplace_plan_benchmark_ratiostate_marketplace_bronze_probabilitystate_marketplace_bronze_to_benchmark_ratioData 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 -qpytest policyengine_us_data/tests/test_calibration/test_unified_matrix_builder.py -qSmoke test:
selected_marketplace_plan_premium_proxy,used_aca_ptc, andunused_aca_ptcused_aca_ptc + unused_aca_ptc == aca_ptcheld exactly in the published H5 auditNotes
This PR is additive plumbing. It does not:
policyengine-usA follow-up rules PR in
policyengine-uscan expose the new tax-unit variables as first-class model variables.