Agent skill
policyengine-healthcare
Healthcare program modeling in PolicyEngine-US — Medicaid, ACA marketplace, CHIP, and Medicare. Covers encoding rules, running analyses, and navigating the unique complexity of US healthcare programs. Triggers: "healthcare", "health insurance", "Medicaid", "ACA", "CHIP", "Medicare", "marketplace", "premium tax credit", "APTC", "PTC", "SLCSP", "benchmark plan", "rating area", "age curve", "family tier", "coverage gap", "Medicaid expansion", "MAGI", "medicaid_magi", "aca_magi", "medicaid_income_level", "medicaid_category", "enrollment", "takeup", "take-up", "per capita", "CSR", "cost sharing", "insurance premium", "second lowest silver", "required contribution percentage", "42 CFR", "IRC 36B", "categorical eligibility", "expansion adult", "healthcare reform", "healthcare analysis", "health policy".
Install this agent skill to your Project
npx add-skill https://github.com/PolicyEngine/policyengine-claude/tree/main/skills/domain-knowledge/policyengine-healthcare-skill
SKILL.md
PolicyEngine healthcare programs
Scope: This skill covers the healthcare domain — Medicaid, ACA marketplace, CHIP, and Medicare as modeled in PolicyEngine-US. For general PolicyEngine-US patterns, see the
policyengine-usskill. For variable/parameter implementation patterns, see thepolicyengine-variable-patternsandpolicyengine-parameter-patternsskills.
For users
What healthcare programs does PolicyEngine model?
PolicyEngine-US models four interconnected healthcare programs:
| Program | What it does | Key variable |
|---|---|---|
| Medicaid | Free/low-cost coverage for low-income individuals | medicaid |
| ACA marketplace | Subsidized private insurance via premium tax credits | aca_ptc |
| CHIP | Children's health coverage above Medicaid thresholds | per_capita_chip |
| Medicare | Coverage for seniors and disabled individuals | medicare |
These programs are mutually exclusive by design — Medicaid eligibility disqualifies you from ACA subsidies, and CHIP covers children who don't qualify for Medicaid. This interconnection is a key modeling challenge.
Why healthcare is different from other benefits
Most benefit programs (SNAP, TANF, EITC) have a single income test and a single benefit amount. Healthcare programs are different:
- 9 eligibility categories for Medicaid alone (infant, young child, older child, young adult, adult expansion, parent, pregnant, SSI recipient, senior/disabled)
- 51 different Medicaid programs — every state sets its own income limits and rules
- ~500 ACA rating areas — premiums vary by geographic zone, not just by state
- Actuarial calculations — ACA subsidies depend on the second-lowest silver plan premium in your area, adjusted by age
- Program interactions — losing Medicaid doesn't automatically mean gaining ACA access (the "coverage gap")
For analysts
Healthcare variables reference
Medicaid
| Variable | Entity | Description |
|---|---|---|
medicaid |
person | Annual Medicaid benefit amount |
is_medicaid_eligible |
person | Overall eligibility (combines all checks) |
medicaid_enrolled |
person | Actually enrolled (eligibility x take-up) |
medicaid_category |
person | Enum: SSI_RECIPIENT, INFANT, YOUNG_CHILD, OLDER_CHILD, PREGNANT, PARENT, YOUNG_ADULT, ADULT, SENIOR_OR_DISABLED, NONE |
medicaid_income_level |
person | Person's tax unit MAGI as fraction of FPL |
medicaid_magi |
tax_unit | Modified AGI for Medicaid (= AGI + state additions) |
takes_up_medicaid_if_eligible |
person | Pseudo-random take-up flag (default rate: 93%) |
medicaid_cost |
person | Per-capita cost by eligibility group and state |
ACA marketplace
| Variable | Entity | Description |
|---|---|---|
aca_ptc |
tax_unit | Annual premium tax credit amount |
is_aca_ptc_eligible |
person | PTC eligibility (income 100-400%+ FPL, no other coverage) |
aca_magi |
tax_unit | Delegates to medicaid_magi via adds = ["medicaid_magi"] |
aca_magi_fraction |
tax_unit | Income as percentage of FPL |
aca_required_contribution_percentage |
tax_unit | Household's required premium contribution rate |
slcsp |
tax_unit | Second-lowest silver plan premium (monthly, definition_period=MONTH). Returns $0 for ineligible people — the model skips the calculation when someone doesn't qualify for ACA. To get the unsubsidized benchmark premium regardless of eligibility, look at the underlying rating area cost parameters directly. |
takes_up_aca_if_eligible |
tax_unit | Take-up flag (default rate varies) |
CHIP
| Variable | Entity | Description |
|---|---|---|
per_capita_chip |
person | CHIP benefit per capita |
is_chip_eligible |
person | CHIP eligibility |
chip_category |
person | Enum: CHILD, PREGNANT_STANDARD, PREGNANT_FCEP, NONE |
Household setup for healthcare analysis
Healthcare calculations require the state to be specified (unlike some federal-only programs):
from policyengine_us import Simulation
situation = {
"people": {
"person1": {
"age": {"2026": 35},
"employment_income": {"2026": 25_000},
"is_tax_unit_head": {"2026": True},
},
"person2": {
"age": {"2026": 8},
"is_tax_unit_dependent": {"2026": True},
},
},
"tax_units": {"tax_unit": {"members": ["person1", "person2"]}},
"families": {"family": {"members": ["person1", "person2"]}},
"spm_units": {"spm_unit": {"members": ["person1", "person2"]}},
"marital_units": {"marital_unit": {"members": ["person1"]}},
"households": {
"household": {
"members": ["person1", "person2"],
"state_name": {"2026": "NY"},
}
},
}
sim = Simulation(situation=situation)
# Check Medicaid eligibility at person level
medicaid_eligible = sim.calculate("is_medicaid_eligible", 2026)
# Check ACA PTC at tax unit level
aca_ptc = sim.calculate("aca_ptc", 2026)
Healthcare reform modeling
Medicaid expansion repeal (parameter modification)
from policyengine_core.reforms import Reform
from policyengine_core.periods import instant
YEAR = 2026
def create_medicaid_expansion_repeal(state="UT"):
"""Remove adult Medicaid expansion by setting income limit to -inf."""
def modify_parameters(parameters):
parameters.gov.hhs.medicaid.eligibility.categories.adult.income_limit[state].update(
start=instant(f"{YEAR}-01-01"),
stop=instant("2100-12-31"),
value=float("-inf"),
)
return parameters
class reform(Reform):
def apply(self):
self.modify_parameters(modify_parameters)
return reform
IRA subsidy extension (variable-override reform)
The most common ACA policy question: extending the IRA-enhanced subsidies beyond their 2025 sunset. Reform.from_dict() does not work for ACA contribution rate parameters because they are list-valued (threshold + initial + final arrays). Use a variable-override reform instead:
from policyengine_us import Microsimulation
from policyengine_core.reforms import Reform
ANALYSIS_YEAR = 2026 # Don't use "YEAR" — it shadows model_api.YEAR
# Look up actual parameter values to build the reform
from policyengine_us import CountryTaxBenefitSystem
p = CountryTaxBenefitSystem().parameters
contrib = p.gov.aca.required_contribution_percentage
# Check what values look like pre/post sunset
print("2025 initial rates:", contrib.initial("2025-01-01")) # IRA-enhanced
print("2026 initial rates:", contrib.initial("2026-01-01")) # Post-sunset (higher)
# Option A: Use Reform.from_dict for the scalar parameters that DO work
reform = Reform.from_dict({
# The 400% FPL cap removal (IRA made subsidies available above 400%)
'gov.aca.ptc_income_eligibility[2].amount': {
f'{ANALYSIS_YEAR}-01-01.2100-12-31': True
},
}, 'policyengine_us')
# Option B: For contribution rates (list-valued), use modify_parameters
from policyengine_core.periods import instant
def create_ira_extension():
def modify_parameters(parameters):
# Restore IRA-era contribution percentages
# You need to set each bracket's initial and final rates
for bracket_param in ['initial', 'final']:
getattr(
parameters.gov.aca.required_contribution_percentage,
bracket_param,
).update(
start=instant(f"{ANALYSIS_YEAR}-01-01"),
stop=instant("2100-12-31"),
value=getattr(contrib, bracket_param)("2025-01-01"),
)
return parameters
class reform(Reform):
def apply(self):
self.modify_parameters(modify_parameters)
return reform
Also see existing reforms in policyengine_us/reforms/aca/ for production examples of variable-override ACA reforms.
Population-level healthcare analysis
from policyengine_us import Microsimulation
import numpy as np
YEAR = 2026
# Use state-calibrated dataset for state-level analysis (much more accurate)
sim = Microsimulation(
dataset="hf://policyengine/policyengine-us-data/states/UT.h5"
)
# Calculate baseline
weights = sim.calculate("person_weight", YEAR).values
medicaid_enrolled = sim.calculate("medicaid_enrolled", YEAR).values
aca_ptc = sim.calculate("aca_ptc", YEAR, map_to="person").values
# Weighted population counts
total_medicaid = (weights * medicaid_enrolled.astype(float)).sum()
total_aca_spending = (weights * aca_ptc).sum()
print(f"Medicaid enrollment: {total_medicaid:,.0f}")
print(f"Total ACA PTC spending: ${total_aca_spending:,.0f}")
Common healthcare analysis patterns
Tracking coverage transitions
When modeling reforms that change Medicaid eligibility, track where people go:
baseline_medicaid = baseline.calculate("medicaid_enrolled", YEAR).values
reform_medicaid = reform_sim.calculate("medicaid_enrolled", YEAR).values
reform_ptc_eligible = reform_sim.calculate("is_aca_ptc_eligible", YEAR).values
loses_medicaid = baseline_medicaid & ~reform_medicaid
gains_aca = loses_medicaid & reform_ptc_eligible
coverage_gap = loses_medicaid & ~reform_ptc_eligible # Below 100% FPL, no ACA access
print(f"Lose Medicaid: {(weights * loses_medicaid.astype(float)).sum():,.0f}")
print(f"Transition to ACA: {(weights * gains_aca.astype(float)).sum():,.0f}")
print(f"Fall into coverage gap: {(weights * coverage_gap.astype(float)).sum():,.0f}")
Distributional analysis by income
income_level = sim.calculate("medicaid_income_level", YEAR).values
# Group by FPL brackets
brackets = [(0, 1.0), (1.0, 1.38), (1.38, 2.0), (2.0, 4.0)]
for low, high in brackets:
mask = (income_level >= low) & (income_level < high)
enrolled = (weights * mask * medicaid_enrolled.astype(float)).sum()
total = (weights * mask.astype(float)).sum()
print(f"{low*100:.0f}-{high*100:.0f}% FPL: {enrolled:,.0f} / {total:,.0f}")
Known pitfalls
PTC does not flow into household_net_income (biggest time sink)
The microsimulation skill says to use household_net_income change as the budgetary cost measure. This does not work for ACA premium tax credit reforms. The PTC is classified as an in-kind health benefit, not a refundable tax credit, so it flows through a separate chain:
aca_ptc → premium_tax_credit → household_health_benefits → household_benefits
There is a toggle parameter gov.simulation.include_health_benefits_in_net_income that defaults to False. With the default, PTC changes produce a $0 change in household_net_income.
How to measure PTC impact instead:
# Option 1: Use household_net_income_including_health_benefits
baseline_hni = baseline.calc("household_net_income_including_health_benefits", period=YEAR)
reformed_hni = reformed.calc("household_net_income_including_health_benefits", period=YEAR)
cost = (reformed_hni - baseline_hni).sum()
# Option 2: Measure the PTC change directly
baseline_ptc = baseline.calc("aca_ptc", period=YEAR).sum()
reformed_ptc = reformed.calc("aca_ptc", period=YEAR).sum()
ptc_cost = reformed_ptc - baseline_ptc
# Option 3: Enable the toggle in the reform
# Add gov.simulation.include_health_benefits_in_net_income = True
# Then household_net_income will include PTC
"IRA reform" means extending IRA-era ACA subsidy brackets
In PolicyEngine context, "IRA reform" is shorthand for extending the Inflation Reduction Act's enhanced ACA premium tax credit brackets beyond their 2025 sunset. The IRA (2022) temporarily expanded ACA subsidies by lowering required contribution percentages and removing the 400% FPL subsidy cliff. These enhancements are scheduled to expire — "IRA reform" typically means making them permanent or extending them.
See the variable-override reform example below for how to implement this.
YEAR constant shadows model_api.YEAR
If you define YEAR = 2026 in the same file where you also define a reform variable class, it shadows the YEAR period constant imported from model_api. This breaks definition_period = YEAR on variable classes:
# ❌ This breaks — YEAR is now 2026 (int), not the period constant
from policyengine_us.model_api import *
YEAR = 2026 # Shadows model_api.YEAR
class my_reform_variable(Variable):
definition_period = YEAR # Gets 2026, not the YEAR period constant!
# ✅ Use a different name for the analysis year
from policyengine_us.model_api import *
ANALYSIS_YEAR = 2026
class my_reform_variable(Variable):
definition_period = YEAR # Gets the period constant from model_api
slcsp returns $0 for ineligible people
The slcsp variable (second-lowest silver plan premium) returns $0 when the person is not ACA-eligible — the model skips the premium lookup entirely. If you need the unsubsidized benchmark premium for comparison purposes (e.g., "what would this person pay without subsidies?"), you can't just read slcsp. Instead, look up the premium from the rating area cost parameters directly:
from policyengine_us import CountryTaxBenefitSystem
p = CountryTaxBenefitSystem().parameters
# state_rating_area_cost is indexed by state and rating area
p.gov.aca.state_rating_area_cost("2026-01-01")
healthcare_benefit_value entity mapping in population analysis
healthcare_benefit_value is a household-level variable that aggregates a tax-unit-level variable (aca_ptc). When you use map_to='person', the benefit value gets spread across all household members, including those not in the affected tax unit. This can produce a "mystery group" that appears to gain healthcare benefit value but shows no change in aca_ptc. Measure PTC impact at the tax-unit level when possible.
Use state datasets for state analysis
The national CPS dataset can give implausible state-level results. For example, national CPS showed 76% employer-sponsored insurance at 100-138% FPL in Utah — the state-calibrated dataset gives much more realistic estimates.
# National (less accurate for state analysis)
sim = Microsimulation(dataset="hf://policyengine/policyengine-us-data/enhanced_cps_2024.h5")
# State-calibrated (preferred for state analysis)
sim = Microsimulation(dataset="hf://policyengine/policyengine-us-data/states/UT.h5")
California ACA rating area bug
LA county's rating area can cause errors in California simulations. Workaround:
import numpy as np
try:
aca_ptc = sim.calculate("aca_ptc", YEAR)
except Exception as e:
if state == "CA":
sim.set_input("in_la", YEAR, np.zeros(n_households, dtype=bool))
aca_ptc = sim.calculate("aca_ptc", YEAR)
The coverage gap
People below 100% FPL who lose Medicaid may not qualify for ACA premium tax credits (which start at 100% FPL in non-expansion states). Always check for this when modeling Medicaid eligibility changes.
Medicaid category transitions
When removing one Medicaid eligibility pathway (e.g., adult expansion), some people may remain eligible through a different category (e.g., parent Medicaid). Track medicaid_category in both baseline and reform to identify true coverage loss vs. category reassignment.
For contributors
Healthcare review checklist
When reviewing healthcare PRs, check these domain-specific items in addition to the standard review checklist:
Program interactions:
- If Medicaid eligibility changed, verify downstream ACA effects (is_aca_ptc_eligible checks Medicaid status)
- If CHIP eligibility changed, verify it still excludes Medicaid-eligible children
- Check that mutual exclusion logic in is_aca_ptc_eligible includes all relevant coverage sources
Eligibility structure:
- New Medicaid categories use the
_fc/_nfcsplit pattern - Category precedence order in medicaid_category.py is preserved (mandatory before optional, per 42 CFR 435.119)
- Income variable uses correct MAGI definition (medicaid_magi for Medicaid/ACA, not raw AGI)
Geographic variation:
- State-specific Medicaid income limits use the correct parameter path (gov.hhs.medicaid.eligibility.categories.[category].income_limit.[STATE])
- ACA changes check whether affected states use default federal age curves or custom curves (AL, DC, MA, MN, MS, OR, UT) or family tiers (NY, VT)
- SLCSP updates cover all affected rating areas
Temporal correctness:
- ACA calculations use prior-year FPL where required
- SLCSP premiums handled as monthly values (not accidentally annualized)
- ARPA/IRA subsidy enhancement sunset dates are correct in parameter timeline
Take-up and costs:
- Take-up rates are parameterized (not hard-coded)
- Cost allocation uses calibrated per-capita values by eligibility group and state
- Enrollment calibration targets updated if eligibility rules changed
Code organization
Healthcare variables live in three directories under policyengine_us/variables/gov/:
gov/hhs/medicaid/ # ~44 variable files
gov/aca/ # ~24 variable files
gov/hhs/chip/ # 8 variable files
gov/hhs/medicare/ # Parts A, B, savings programs
Variable naming patterns
| Pattern | Example | Purpose |
|---|---|---|
is_[program]_eligible |
is_medicaid_eligible |
Overall eligibility flag |
[program]_category |
medicaid_category |
Enum categorization |
is_[category]_for_[program] |
is_adult_for_medicaid |
Category-specific eligibility |
is_[category]_for_[program]_fc |
is_adult_for_medicaid_fc |
Financial criteria only |
is_[category]_for_[program]_nfc |
is_adult_for_medicaid_nfc |
Non-financial criteria only |
[program]_magi |
medicaid_magi |
Income measure |
[program]_income_level |
medicaid_income_level |
Income as fraction of FPL |
takes_up_[program]_if_eligible |
takes_up_medicaid_if_eligible |
Take-up modeling |
[program]_cost |
medicaid_cost |
Benefit cost/amount |
per_capita_[program] |
per_capita_chip |
Per-person cost |
The _fc / _nfc pattern
Healthcare eligibility splits financial and non-financial criteria into separate variables. This is important because:
- Testability — you can test income thresholds independently from age/demographic requirements
- Reform modeling — reforms often change only one dimension (e.g., raising income limits without changing age ranges)
- Clarity — reviewers can verify each criterion against its regulatory source
# is_adult_for_medicaid.py combines both using a class attribute (not a formula):
class is_adult_for_medicaid(Variable):
# all_of_variables as class attribute — no formula method needed
formula = all_of_variables([
"is_adult_for_medicaid_fc", # Income < state limit
"is_adult_for_medicaid_nfc", # Age 19-64, not pregnant
])
Medicaid categorical hierarchy
Categories are evaluated in regulatory precedence order (42 CFR 435.119). Mandatory groups first, then optional:
# From medicaid_category.py — ORDER MATTERS
variable_to_category = dict(
is_ssi_recipient_for_medicaid=MedicaidCategory.SSI_RECIPIENT, # 1st
is_infant_for_medicaid=MedicaidCategory.INFANT, # 2nd
is_young_child_for_medicaid=MedicaidCategory.YOUNG_CHILD, # 3rd
is_older_child_for_medicaid=MedicaidCategory.OLDER_CHILD, # 4th
is_pregnant_for_medicaid=MedicaidCategory.PREGNANT, # 5th
is_parent_for_medicaid=MedicaidCategory.PARENT, # 6th
is_young_adult_for_medicaid=MedicaidCategory.YOUNG_ADULT, # 7th
is_adult_for_medicaid=MedicaidCategory.ADULT, # 8th (expansion)
is_senior_or_disabled_for_medicaid=MedicaidCategory.SENIOR_OR_DISABLED, # Last
)
A person matched to an earlier category is assigned that category even if they also qualify for a later one. This means adult expansion is always the residual category.
Parameter structure
Healthcare parameters are deeply nested with state-level overrides:
parameters/gov/hhs/medicaid/
├── eligibility/
│ └── categories/
│ ├── adult/income_limit.yaml # State-by-state limits
│ ├── infant/income_limit.yaml
│ ├── parent/income_limit.yaml
│ └── [5 more categories]
├── income/modification.yaml # AGI adjustments
├── takeup_rate.yaml # 0.93 nationally
└── emergency_medicaid/enabled.yaml
parameters/gov/aca/
├── state_rating_area_cost.yaml # 1,565 lines — SLCSP by state x rating area
├── age_curves/
│ ├── default.yaml # Federal 3:1 ratio
│ ├── al.yaml, dc.yaml, ... # 7 states with custom curves
│ └── ny.yaml, vt.yaml # Family tier states
├── required_contribution_percentage/ # LIST-VALUED — not scalar!
│ ├── threshold.yaml # FPL bracket boundaries (list)
│ ├── initial.yaml # Initial contribution rates by bracket (list)
│ └── final.yaml # Final contribution rates by bracket (list)
│ # These three files form parallel arrays. Reform.from_dict() cannot modify
│ # list-valued parameters — use modify_parameters() instead.
└── ptc_income_eligibility.yaml # 100-400%+ FPL range (bracket-indexed)
ACA geographic complexity
The ACA premium calculation involves three layers of geographic variation:
- Rating area — ~500 state+rating-area zones across the US, each with its own SLCSP benchmark premium
- Age curves — most states use the federal 3:1 age rating; 7 states (AL, DC, MA, MN, MS, OR, UT) use custom curves; NY and VT use family tiers instead of age rating entirely
- State-specific rules — max child count for subsidies, child age definitions
When adding or updating ACA parameters, check whether the state uses the default federal rules or has its own.
Program interaction rules
Healthcare programs check for mutual exclusion:
# In is_aca_ptc_eligible.py
INELIGIBLE_COVERAGE = [
"is_medicaid_eligible",
"is_chip_eligible",
# ... other coverage sources
]
is_coverage_eligible = add(person, period, INELIGIBLE_COVERAGE) == 0
When modifying one program's eligibility, always verify the downstream effects on other programs. Removing Medicaid expansion, for example, shifts people to ACA eligibility (if above 100% FPL) or into a coverage gap (if below).
Healthcare test patterns
Healthcare tests require attention to program interactions and categorical complexity that other benefit programs don't have.
Testing category precedence:
Verify that a person eligible for multiple Medicaid categories gets assigned the highest-precedence one:
- name: Case 1, pregnant adult assigned PREGNANT not ADULT.
period: 2026-01
absolute_error_margin: 0.1
input:
people:
person1:
age: 25
is_pregnant: true
employment_income: 15_000
households:
household1:
state_code_str: NY
output:
medicaid_category: PREGNANT # Not ADULT, even though income qualifies for expansion
Testing program mutual exclusion:
Verify that Medicaid-eligible people are excluded from ACA PTC:
- name: Case 2, Medicaid eligible person gets no ACA PTC.
period: 2026
absolute_error_margin: 0.1
input:
people:
person1:
age: 30
employment_income: 15_000
is_tax_unit_head: true
households:
household1:
state_code_str: NY
output:
is_medicaid_eligible: true
aca_ptc: 0
Testing the _fc / _nfc split independently:
Test financial and non-financial criteria separately to isolate failures:
# Financial criteria only
- name: Case 3, adult income below Medicaid limit.
period: 2026-01
absolute_error_margin: 0.1
input:
people:
person1:
age: 30
employment_income: 15_000
households:
household1:
state_code_str: NY
output:
is_adult_for_medicaid_fc: true
# Non-financial criteria only
- name: Case 4, person in adult age range.
period: 2026-01
absolute_error_margin: 0.1
input:
people:
person1:
age: 30
is_pregnant: false
output:
is_adult_for_medicaid_nfc: true
Testing coverage transitions in reforms:
When testing reforms that change eligibility, verify where people land:
# In integration tests, check all three outcomes:
# 1. Still has coverage (different program)
# 2. Transitions to ACA
# 3. Falls into coverage gap (below 100% FPL, no ACA access)
Testing state-specific ACA rules:
For states with custom age curves or family tiers, include state-specific test cases:
# NY uses family tiers instead of age rating
- name: Case 5, NY family tier premium.
period: 2026
absolute_error_margin: 1
input:
people:
person1:
age: 35
employment_income: 40_000
is_tax_unit_head: true
person2:
age: 8
is_tax_unit_dependent: true
households:
household1:
state_code_str: NY
output:
slcsp: 0 # Verify against NY family tier rates, not age-based
Take-up modeling
All healthcare programs use pseudo-random seeding for take-up:
class takes_up_medicaid_if_eligible(Variable):
def formula(person, period, parameters):
seed = person("medicaid_take_up_seed", period) # Random 0-1
takeup_rate = parameters(period).gov.hhs.medicaid.takeup_rate
return seed < takeup_rate
Default take-up rates: Medicaid ~93%, ACA PTC ~62-67%. These are parameterized and can be modified in reforms.
Cost allocation
Healthcare costs use per-capita lookups calibrated from MACPAC and CMS data, broken down by eligibility group and state:
class medicaid_cost_if_enrolled(Variable):
def formula(person, period, parameters):
group = person("medicaid_group", period) # Enum
state = person.household("state_code", period)
per_capita = parameters(period).calibration.gov.hhs.medicaid.spending
return per_capita.by_eligibility_group[group][state]
Temporal considerations
- ACA uses prior-year FPL: Income eligibility is measured against the previous year's federal poverty guidelines
- SLCSP premiums are monthly: But benefits are typically calculated annually — watch for period mismatches
- Contribution rates sunset: ARPA/IRA enhanced subsidies have expiration dates encoded in the parameter timeline
Resources
- Medicaid variables:
policyengine_us/variables/gov/hhs/medicaid/ - ACA variables:
policyengine_us/variables/gov/aca/ - CHIP variables:
policyengine_us/variables/gov/hhs/chip/ - Healthcare parameters:
policyengine_us/parameters/gov/hhs/medicaid/,policyengine_us/parameters/gov/aca/ - Calibration data:
policyengine_us/parameters/calibration/gov/hhs/ - Analysis examples:
analysis-notebooks/us/healthcare/,analysis-notebooks/us/medicaid/,analysis-notebooks/us/states/ut/ - Legal references: 42 CFR 435.119 (Medicaid categories), IRC 36B (premium tax credit)
Didn't find tool you were looking for?