diff --git a/changelog.d/md-premium-assistance-exact-fpl-ratio.fixed.md b/changelog.d/md-premium-assistance-exact-fpl-ratio.fixed.md new file mode 100644 index 00000000000..fb5c4a34f83 --- /dev/null +++ b/changelog.d/md-premium-assistance-exact-fpl-ratio.fixed.md @@ -0,0 +1 @@ +Decide Maryland Premium Assistance's 400% income ceiling on the exact ratio of ACA MAGI to the prior-year poverty guideline instead of the truncated `aca_magi_fraction` (MHBE PY2026 design: "No state subsidy above 400% FPL"; PY2027: "Eligibility: 400% FPL or below"). Under current law the federal premium tax credit gate already ends at 400%, so this changes results only when a reform extends the federal credit above 400%, where incomes from 400.003% to 400.994% were eligible. diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/mhbe/premium_assistance/md_premium_assistance.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/mhbe/premium_assistance/md_premium_assistance.yaml index dc03a0ca2b0..c4124069b40 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/mhbe/premium_assistance/md_premium_assistance.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/mhbe/premium_assistance/md_premium_assistance.yaml @@ -229,6 +229,7 @@ md_premium_assistance: 0 - name: Case 9, program not in effect in 2025 -> 0. + # 250% of the 2024 one-person guideline ($15,060), the prior year for 2025. period: 2025 absolute_error_margin: 0.01 input: @@ -239,7 +240,7 @@ tax_units: tax_unit: members: [person1] - aca_magi: 39_125 + aca_magi: 37_650 aca_magi_fraction: 2.50 aca_required_contribution_percentage: 0.0844 slcsp: 6_000 @@ -252,6 +253,7 @@ md_premium_assistance: 0 - name: Case 10, program sunset in 2028 -> 0. + # The prior-year guideline is pinned so the supplied 2.50 matches aca_magi. period: 2028 absolute_error_margin: 0.01 input: @@ -263,6 +265,8 @@ tax_unit: members: [person1] aca_magi: 39_125 + tax_unit_fpg: + 2027: 15_650 aca_magi_fraction: 2.50 aca_required_contribution_percentage: 0.0844 slcsp: 6_000 @@ -306,7 +310,7 @@ # COMAR .04B: max(0, (6_000 - 3_500) - md_pct * 0) = 2_500. (This case # expected 0 under the older (federal - md) * income reading; on main it # passed only because the unit, with no gross income, computed as a - # non-filer.) + # non-filer.) Zero MAGI derives a 0% FPL fraction, so md_pct is 0. period: 2026 absolute_error_margin: 0.01 input: @@ -318,7 +322,6 @@ tax_unit: members: [person1] aca_magi: 0 - aca_magi_fraction: 1.50 aca_required_contribution_percentage: 0.0419 slcsp: 6_000 aca_ptc: 3_500 @@ -408,9 +411,10 @@ output: md_premium_assistance: 0 -- name: Case 16, single age 40 at 225% FPL -> 1061.50 (interpolated 200-250% band). +- name: Case 16, single age 40 at 225% FPL -> 1052.77 (interpolated 200-250% band). # md target 2.25 FPL = 0.02 + 0.5*(0.0622-0.02) = 0.0411. COMAR .04B: - # max(0, (6_000 - 3_500) - 0.0411 * 35_000) = 2_500 - 1_438.50 = 1_061.50. + # 225% of the 2025 one-person guideline ($15,650) is 35_212.50. + # max(0, (6_000 - 3_500) - 0.0411 * 35_212.50) = 2_500 - 1_447.23 = 1_052.77. period: 2026 absolute_error_margin: 0.01 input: @@ -421,7 +425,7 @@ tax_units: tax_unit: members: [person1] - aca_magi: 35_000 + aca_magi: 35_212.50 aca_magi_fraction: 2.25 aca_required_contribution_percentage: 0.0752 slcsp: 6_000 @@ -432,13 +436,14 @@ members: [person1] state_code: MD output: - md_premium_assistance: 1_061.50 + md_premium_assistance: 1_052.77 -- name: Case 17, multi-member mixed APTC eligibility -> 929.39 (non-enrollee YA excluded). +- name: Case 17, multi-member mixed APTC eligibility -> 1256.31 (non-enrollee YA excluded). # age-40 enrollee + age-30 NON-enrollee at 300% FPL. Unit is eligible (one enrollee), # and the age-30 non-enrollee does not trigger the young adult reduction, so - # md target = 7.98%. COMAR .04B: - # max(0, (9_000 - 4_324) - 0.0798 * 46_950) = 4_676 - 3_746.61 = 929.39. + # md target = 7.98%. 300% of the 2025 two-person guideline ($21,150) is 63_450; + # self-consistent APTC = 9_000 - 0.0996 * 63_450 = 2_680.38. COMAR .04B: + # max(0, (9_000 - 2_680.38) - 0.0798 * 63_450) = 6_319.62 - 5_063.31 = 1_256.31. period: 2026 absolute_error_margin: 0.01 input: @@ -452,18 +457,18 @@ tax_units: tax_unit: members: [person1, person2] - aca_magi: 46_950 + aca_magi: 63_450 aca_magi_fraction: 3.00 aca_required_contribution_percentage: 0.0996 slcsp: 9_000 - aca_ptc: 4_324 + aca_ptc: 2_680.38 tax_unit_is_filer: true households: household: members: [person1, person2] state_code: MD output: - md_premium_assistance: 929.39 + md_premium_assistance: 1_256.31 - name: Case 18, interior interpolation in the 300-400% band with a DERIVED target %. # aca_magi_fraction 3.50 -> md_pct interpolates initial[4]=0.0798 (at 3.0) to @@ -497,10 +502,11 @@ output: md_premium_assistance: 742.20 # self-consistent APTC: equals the old reading -- name: Case 19, second in-effect year (2027) mirrors the 2026 250% FPL case -> 66.43. +- name: Case 19, second in-effect year (2027) mirrors the 2026 250% FPL case -> 18.22. # Same forced-input shape as Case 3 but at period 2027, proving the program is # still in effect in its second plan year (distinct from the 2028 sunset). - # COMAR .04B: max(0, (6_000 - 3_500) - 0.0622 * 39_125) = 2_500 - 2_433.575 = 66.425. + # 250% of the 2026 one-person guideline ($15,960), the prior year for 2027, is + # 39_900. COMAR .04B: max(0, (6_000 - 3_500) - 0.0622 * 39_900) = 2_500 - 2_481.78 = 18.22. period: 2027 absolute_error_margin: 0.01 input: @@ -511,7 +517,7 @@ tax_units: tax_unit: members: [person1] - aca_magi: 39_125 + aca_magi: 39_900 aca_magi_fraction: 2.50 aca_required_contribution_percentage: 0.0844 slcsp: 6_000 @@ -522,7 +528,7 @@ members: [person1] state_code: MD output: - md_premium_assistance: 66.425 + md_premium_assistance: 18.22 - name: Case 20, negative aca_magi -> income floored before multiplying (no sign-flip). # The formula floors income at 0 (max_(aca_magi, 0)), so a negative aca_magi @@ -556,10 +562,11 @@ - name: Case 21, young-adult floor drives target % to 0 -> subsidy is the full post-APTC premium. # Age 25 (2.5pp reduction) at 175% FPL where base target = 0.01 (interpolated # 0%@1.5 -> 2%@2.0, position 0.5). 0.01 - 0.025 < 0 -> md_pct floors at 0. + # 175% of the 2025 one-person guideline ($15,650) is 27_387.50. # SYNTHETIC federal % 0.05 (not the true schedule); self-consistent APTC = - # slcsp 6_000 - 0.05 * 27_000 = 6_000 - 1_350 = 4_650. With md_pct 0 the MD - # contribution is 0, so the subsidy is the full post-APTC premium balance: - # max(0, (6_000 - 4_650) - 0 * 27_000) = 1_350. Filer inputs supplied. + # slcsp 6_000 - 0.05 * 27_387.50 = 6_000 - 1_369.375 = 4_630.625. With md_pct 0 + # the MD contribution is 0, so the subsidy is the full post-APTC premium balance: + # max(0, (6_000 - 4_630.625) - 0 * 27_387.50) = 1_369.375. Filer inputs supplied. period: 2026 absolute_error_margin: 0.01 input: @@ -567,21 +574,21 @@ person1: age: 25 is_aca_ptc_eligible: true - employment_income: 27_000 + employment_income: 27_387.50 tax_units: tax_unit: members: [person1] - aca_magi: 27_000 + aca_magi: 27_387.50 aca_magi_fraction: 1.75 aca_required_contribution_percentage: 0.05 slcsp: 6_000 - aca_ptc: 4_650 + aca_ptc: 4_630.625 households: household: members: [person1] state_code: MD output: - md_premium_assistance: 1_350 # md_pct floored to 0 -> full post-APTC premium + md_premium_assistance: 1_369.375 # md_pct floored to 0 -> full post-APTC premium # ---- C1 formula-correction boundary cases (post-COMAR .04B re-derivation) ---- diff --git a/policyengine_us/tests/policy/baseline/gov/states/md/mhbe/premium_assistance/md_premium_assistance_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/md/mhbe/premium_assistance/md_premium_assistance_eligible.yaml index 2d039997f57..5b0c042a7f6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/md/mhbe/premium_assistance/md_premium_assistance_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/md/mhbe/premium_assistance/md_premium_assistance_eligible.yaml @@ -1,8 +1,12 @@ # Unit tests for Maryland Premium Assistance eligibility. # Eligible when: program in effect (PY2026-2027), household in MD, at least one -# tax-unit member is federal-APTC-eligible, and ACA MAGI <= 400% FPL. -# is_aca_ptc_eligible and aca_magi_fraction are provided directly so the gate -# logic is tested in isolation from the premium-data lookup. +# tax-unit member is federal-APTC-eligible, and ACA MAGI <= 400% FPL ("No state +# subsidy above 400% FPL"; PY2027 "400% FPL or below"), on the exact ratio of +# ACA MAGI to the prior-year guideline, not the truncated aca_magi_fraction. +# is_aca_ptc_eligible is provided directly so the gate logic is tested in +# isolation from the premium-data lookup. Supplied-income cases pin the +# prior-year guideline to $10,000; income-derived cases use the actual 2025 +# one-person guideline, $15,650. - name: Case 1, MD APTC-eligible unit at 250% FPL in 2026 is eligible. period: 2026 @@ -14,7 +18,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -32,7 +38,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 4.01 + aca_magi: 40_100 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -50,7 +58,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 4.00 + aca_magi: 40_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -68,7 +78,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -86,7 +98,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -104,7 +118,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2024: 10_000 households: household: members: [person1] @@ -122,7 +138,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2027: 10_000 households: household: members: [person1] @@ -143,7 +161,9 @@ tax_units: tax_unit: members: [person1, person2] - aca_magi_fraction: 3.00 + aca_magi: 30_000 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1, person2] @@ -164,7 +184,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 0 + aca_magi: 0 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -182,7 +204,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 3.99 + aca_magi: 39_900 + tax_unit_fpg: + 2025: 10_000 households: household: members: [person1] @@ -202,7 +226,9 @@ tax_units: tax_unit: members: [person1] - aca_magi_fraction: 2.50 + aca_magi: 25_000 + tax_unit_fpg: + 2026: 10_000 households: household: members: [person1] @@ -256,3 +282,106 @@ state_code: MD output: md_premium_assistance_eligible: false # derived aca_magi_fraction > 4.00 cap + +- name: Case 14, $62,599 (399.994% of the 2025 guideline) is eligible. + period: 2026 + input: + people: + person1: + age: 40 + is_aca_ptc_eligible: true + tax_units: + tax_unit: + members: [person1] + aca_magi: 62_599 + households: + household: + members: [person1] + state_code: MD + output: + aca_magi_fraction: 3.99 + md_premium_assistance_eligible: true + +- name: Case 15, $62,600 (exactly 400%) is eligible. + period: 2026 + input: + people: + person1: + age: 40 + is_aca_ptc_eligible: true + tax_units: + tax_unit: + members: [person1] + aca_magi: 62_600 + households: + household: + members: [person1] + state_code: MD + output: + aca_magi_fraction: 4.00 + md_premium_assistance_eligible: true + +- name: Case 16, $62,600.47 (400.003%) is ineligible though truncated to 4.00. + period: 2026 + input: + people: + person1: + age: 40 + is_aca_ptc_eligible: true + tax_units: + tax_unit: + members: [person1] + aca_magi: 62_600.47 + households: + household: + members: [person1] + state_code: MD + output: + aca_magi_fraction: 4.00 + md_premium_assistance_eligible: false + +- name: Case 17, $62,755.56 (400.994%) is ineligible though truncated to 4.00. + period: 2026 + input: + people: + person1: + age: 40 + is_aca_ptc_eligible: true + tax_units: + tax_unit: + members: [person1] + aca_magi: 62_755.56 + households: + household: + members: [person1] + state_code: MD + output: + aca_magi_fraction: 4.00 + md_premium_assistance_eligible: false + +- name: Case 18, with the federal credit extended above 400%, 400.003% is still ineligible. + # A reform that keeps the premium tax credit above 400% FPL makes + # is_aca_ptc_eligible true at $62,600.47, so the Maryland ceiling alone decides. + period: 2026 + input: + gov.aca.ptc_income_eligibility[2].amount: true + people: + person1: + age: 40 + is_medicaid_eligible: false + is_chip_eligible: false + is_basic_health_program_eligible: false + is_aca_eshi_eligible: false + is_medicare_eligible: false + tax_units: + tax_unit: + members: [person1] + aca_magi: 62_600.47 + filing_status: SINGLE + households: + household: + members: [person1] + state_code: MD + output: + is_aca_ptc_eligible: [true] + md_premium_assistance_eligible: false diff --git a/policyengine_us/variables/gov/states/md/mhbe/premium_assistance/md_premium_assistance_eligible.py b/policyengine_us/variables/gov/states/md/mhbe/premium_assistance/md_premium_assistance_eligible.py index a03b288a370..adf29164b54 100644 --- a/policyengine_us/variables/gov/states/md/mhbe/premium_assistance/md_premium_assistance_eligible.py +++ b/policyengine_us/variables/gov/states/md/mhbe/premium_assistance/md_premium_assistance_eligible.py @@ -13,17 +13,26 @@ class md_premium_assistance_eligible(Variable): reference = ( "https://regs.maryland.gov/us/md/exec/comar/14.35.21#page=5", "https://mgaleg.maryland.gov/meeting_material/2025/hgo%20-%20134051066649659653%20-%20Combined%20MHBE.MIA%20slides_10.16.2025%20briefing%20to%20HGO&Finance.pdf#page=58", + # MHBE adopted PY2026 and published PY2027 subsidy parameters. + "https://www.marylandhbe.com/wp-content/uploads/2025/08/2026-State-Subsidy-and-Reinsurance-Parameters-8-18-25.pdf#page=30", + "https://www.marylandhbe.com/wp-content/uploads/2026/08/2027-Final-State-Subsidy-and-Estimated-Reinsurance-Parameters.pdf#page=25", ) # A tax unit is eligible for Maryland Premium Assistance when the program # is in effect, at least one member is eligible for the federal ACA # premium tax credit, and household income is at or below the federal - # poverty line limit. + # poverty line limit, on the exact ratio of ACA MAGI to the prior-year + # guideline. def formula(tax_unit, period, parameters): p = parameters(period).gov.states.md.mhbe.premium_assistance in_effect = p.in_effect # At least one member must be eligible for the federal ACA PTC. aptc_eligible = add(tax_unit, period, ["is_aca_ptc_eligible"]) > 0 - magi_frac = tax_unit("aca_magi_fraction", period) - income_eligible = magi_frac <= p.fpl_limit + # The MHBE design sets "No state subsidy above 400% FPL" (PY2026) and + # "Eligibility: 400% FPL or below" (PY2027). Test the exact ratio, not + # the truncated aca_magi_fraction: truncation maps 400.00%-400.99% to + # 4.00, which would admit incomes above 400%. + magi = max_(tax_unit("aca_magi", period), 0) + exact_magi_frac = magi / tax_unit("tax_unit_fpg", period.last_year) + income_eligible = exact_magi_frac <= p.fpl_limit return in_effect & aptc_eligible & income_eligible