diff --git a/changelog.d/marketplace-selected-plan-ptc-proxy.added.md b/changelog.d/marketplace-selected-plan-ptc-proxy.added.md new file mode 100644 index 00000000000..cc999337f0c --- /dev/null +++ b/changelog.d/marketplace-selected-plan-ptc-proxy.added.md @@ -0,0 +1 @@ +Added ACA selected-plan marketplace proxy variables for benchmark ratio inputs and used versus unused premium tax credit analysis. diff --git a/policyengine_us/tests/policy/baseline/gov/aca/ptc/integration.yaml b/policyengine_us/tests/policy/baseline/gov/aca/ptc/integration.yaml index de4d3d88e5a..6b8f68d4196 100644 --- a/policyengine_us/tests/policy/baseline/gov/aca/ptc/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/aca/ptc/integration.yaml @@ -61,3 +61,32 @@ slcsp: 4_911.564 #kff says 4,916 aca_magi_fraction: 1.99 # 30k / 15,060 (approx. prior-year FPL reference) aca_ptc: 4_323.56 + +- name: 2025 aca_ptc_los_angeles_single_selected_plan_ratio + absolute_error_margin: 0.01 + period: 2025 + input: + people: + person1: + age: 40 + employment_income: 30_000 + is_aca_eshi_eligible: false + tax_units: + tax_unit: + members: [person1] + selected_marketplace_plan_benchmark_ratio: 0.8 + households: + household: + members: [person1] + state_fips: 6 # CA + county: LOS_ANGELES_COUNTY_CA + three_digit_zip_code: 902 # 90210 -> rating area 16 in CA + output: + is_medicaid_eligible: [false] + is_aca_ptc_eligible: [true] + slcsp: 4_911.564 #kff says 4,916 + aca_magi_fraction: 1.99 # 30k / 15,060 (approx. prior-year FPL reference) + aca_ptc: 4_323.56 + selected_marketplace_plan_premium_proxy: 3_929.25 + used_aca_ptc: 3_929.25 + unused_aca_ptc: 394.31 diff --git a/policyengine_us/tests/policy/baseline/gov/aca/ptc/selected_marketplace_plan_premium_proxy.yaml b/policyengine_us/tests/policy/baseline/gov/aca/ptc/selected_marketplace_plan_premium_proxy.yaml new file mode 100644 index 00000000000..dab7a5265e7 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/aca/ptc/selected_marketplace_plan_premium_proxy.yaml @@ -0,0 +1,34 @@ +- name: Case 1, selected plan proxy defaults to the benchmark premium. + absolute_error_margin: 0.01 + period: 2025 + input: + is_aca_ptc_eligible: true + slcsp: 1_200 + aca_magi: 25_000 + aca_required_contribution_percentage: 0.04 + output: + selected_marketplace_plan_premium_proxy: 1_200 + +- name: Case 2, selected plan proxy scales with the selected plan ratio. + absolute_error_margin: 0.01 + period: 2025 + input: + is_aca_ptc_eligible: true + slcsp: 10_000 + aca_magi: 20_000 + aca_required_contribution_percentage: 0.04 + selected_marketplace_plan_benchmark_ratio: 0.8 + output: + selected_marketplace_plan_premium_proxy: 8_000 + +- name: Case 3, selected plan proxy is zero when no ACA PTC is available. + absolute_error_margin: 0.01 + period: 2025 + input: + is_aca_ptc_eligible: true + slcsp: 1_200 + aca_magi: 25_000 + aca_required_contribution_percentage: 0.05 + selected_marketplace_plan_benchmark_ratio: 0.8 + output: + selected_marketplace_plan_premium_proxy: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/aca/ptc/used_aca_ptc.yaml b/policyengine_us/tests/policy/baseline/gov/aca/ptc/used_aca_ptc.yaml new file mode 100644 index 00000000000..a4364f01a29 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/aca/ptc/used_aca_ptc.yaml @@ -0,0 +1,41 @@ +- name: Case 1, benchmark-plan selection uses the full ACA PTC. + absolute_error_margin: 0.01 + period: 2025 + input: + is_aca_ptc_eligible: true + slcsp: 1_200 + aca_magi: 25_000 + aca_required_contribution_percentage: 0.04 + output: + aca_ptc: 200 + used_aca_ptc: 200 + unused_aca_ptc: 0 + +- name: Case 2, a bronze-like ratio leaves some ACA PTC unused. + absolute_error_margin: 0.01 + period: 2025 + input: + is_aca_ptc_eligible: true + slcsp: 10_000 + aca_magi: 20_000 + aca_required_contribution_percentage: 0.04 + selected_marketplace_plan_benchmark_ratio: 0.8 + output: + aca_ptc: 9_200 + selected_marketplace_plan_premium_proxy: 8_000 + used_aca_ptc: 8_000 + unused_aca_ptc: 1_200 + +- name: Case 3, used and unused ACA PTC are zero when ACA PTC is zero. + absolute_error_margin: 0.01 + period: 2025 + input: + is_aca_ptc_eligible: true + slcsp: 1_200 + aca_magi: 25_000 + aca_required_contribution_percentage: 0.05 + selected_marketplace_plan_benchmark_ratio: 0.8 + output: + aca_ptc: 0 + used_aca_ptc: 0 + unused_aca_ptc: 0 diff --git a/policyengine_us/variables/gov/aca/ptc/selected_marketplace_plan_benchmark_ratio.py b/policyengine_us/variables/gov/aca/ptc/selected_marketplace_plan_benchmark_ratio.py new file mode 100644 index 00000000000..94cd07a66fa --- /dev/null +++ b/policyengine_us/variables/gov/aca/ptc/selected_marketplace_plan_benchmark_ratio.py @@ -0,0 +1,10 @@ +from policyengine_us.model_api import * + + +class selected_marketplace_plan_benchmark_ratio(Variable): + value_type = float + entity = TaxUnit + label = "Selected marketplace plan premium to benchmark premium ratio" + unit = "/1" + definition_period = YEAR + default_value = 1.0 diff --git a/policyengine_us/variables/gov/aca/ptc/selected_marketplace_plan_premium_proxy.py b/policyengine_us/variables/gov/aca/ptc/selected_marketplace_plan_premium_proxy.py new file mode 100644 index 00000000000..adabed91b4d --- /dev/null +++ b/policyengine_us/variables/gov/aca/ptc/selected_marketplace_plan_premium_proxy.py @@ -0,0 +1,18 @@ +from policyengine_us.model_api import * + + +class selected_marketplace_plan_premium_proxy(Variable): + value_type = float + entity = TaxUnit + label = "Selected marketplace plan premium proxy" + unit = USD + definition_period = YEAR + + def formula(tax_unit, period, parameters): + aca_ptc = tax_unit("aca_ptc", period) + return where( + aca_ptc > 0, + tax_unit("slcsp", period) + * tax_unit("selected_marketplace_plan_benchmark_ratio", period), + 0, + ) diff --git a/policyengine_us/variables/gov/aca/ptc/unused_aca_ptc.py b/policyengine_us/variables/gov/aca/ptc/unused_aca_ptc.py new file mode 100644 index 00000000000..52463350cdf --- /dev/null +++ b/policyengine_us/variables/gov/aca/ptc/unused_aca_ptc.py @@ -0,0 +1,12 @@ +from policyengine_us.model_api import * + + +class unused_aca_ptc(Variable): + value_type = float + entity = TaxUnit + label = "Unused ACA premium tax credit" + unit = USD + definition_period = YEAR + + def formula(tax_unit, period, parameters): + return max_(0, tax_unit("aca_ptc", period) - tax_unit("used_aca_ptc", period)) diff --git a/policyengine_us/variables/gov/aca/ptc/used_aca_ptc.py b/policyengine_us/variables/gov/aca/ptc/used_aca_ptc.py new file mode 100644 index 00000000000..6de35a53b29 --- /dev/null +++ b/policyengine_us/variables/gov/aca/ptc/used_aca_ptc.py @@ -0,0 +1,15 @@ +from policyengine_us.model_api import * + + +class used_aca_ptc(Variable): + value_type = float + entity = TaxUnit + label = "Used ACA premium tax credit" + unit = USD + definition_period = YEAR + + def formula(tax_unit, period, parameters): + return min_( + tax_unit("aca_ptc", period), + tax_unit("selected_marketplace_plan_premium_proxy", period), + )