diff --git a/changelog.d/enforce-401k-elective-deferral-limits.added.md b/changelog.d/enforce-401k-elective-deferral-limits.added.md new file mode 100644 index 00000000000..23a1c808081 --- /dev/null +++ b/changelog.d/enforce-401k-elective-deferral-limits.added.md @@ -0,0 +1 @@ +Cap traditional and Roth 401(k) and 403(b) contributions at the IRC Section 402(g) elective deferral limit, including age-based catch-ups. diff --git a/policyengine_us/parameters/gov/contrib/crfb/surtax/increased_base/sources.yaml b/policyengine_us/parameters/gov/contrib/crfb/surtax/increased_base/sources.yaml index e905d72f77d..38ace3e251f 100644 --- a/policyengine_us/parameters/gov/contrib/crfb/surtax/increased_base/sources.yaml +++ b/policyengine_us/parameters/gov/contrib/crfb/surtax/increased_base/sources.yaml @@ -3,8 +3,8 @@ values: 2010-01-01: # Retirement contributions (IRA, 401k, 403b) - capped_traditional_ira_contributions - - traditional_401k_contributions - - traditional_403b_contributions + - capped_traditional_401k_contributions + - capped_traditional_403b_contributions # HSA contributions (no Archer MSA variable exists) - health_savings_account_ald # Deductible student loan interest diff --git a/policyengine_us/parameters/gov/irs/credits/retirement_saving/qualified_retirement_savings_contributions.yaml b/policyengine_us/parameters/gov/irs/credits/retirement_saving/qualified_retirement_savings_contributions.yaml index c7536233764..7ee62531711 100644 --- a/policyengine_us/parameters/gov/irs/credits/retirement_saving/qualified_retirement_savings_contributions.yaml +++ b/policyengine_us/parameters/gov/irs/credits/retirement_saving/qualified_retirement_savings_contributions.yaml @@ -2,21 +2,21 @@ description: The IRA sums the following elements as qualified retirement savings values: 2018-01-01: - capped_traditional_ira_contributions - - traditional_401k_contributions - - traditional_403b_contributions - capped_roth_ira_contributions - - roth_401k_contributions - - roth_403b_contributions + - capped_traditional_401k_contributions + - capped_traditional_403b_contributions + - capped_roth_401k_contributions + - capped_roth_403b_contributions - self_employed_pension_contributions - able_contributions_person 2026-01-01: - capped_traditional_ira_contributions - - traditional_401k_contributions - - traditional_403b_contributions - capped_roth_ira_contributions - - roth_401k_contributions - - roth_403b_contributions + - capped_traditional_401k_contributions + - capped_traditional_403b_contributions + - capped_roth_401k_contributions + - capped_roth_403b_contributions - self_employed_pension_contributions metadata: diff --git a/policyengine_us/parameters/gov/irs/gross_income/pre_tax_contributions.yaml b/policyengine_us/parameters/gov/irs/gross_income/pre_tax_contributions.yaml index 7b96c227a0d..bf64aff54bf 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/pre_tax_contributions.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/pre_tax_contributions.yaml @@ -3,8 +3,8 @@ values: 2010-01-01: # Assumes all are pre-tax. # Roth 401(k)/403(b) contributions are post-tax and intentionally excluded. - - traditional_401k_contributions - - traditional_403b_contributions + - capped_traditional_401k_contributions + - capped_traditional_403b_contributions # Only explicit pre-tax payroll health premiums reduce taxable wages. - pre_tax_health_insurance_premiums # HSA contributions can be either through pre-tax. diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/k401.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/k401.yaml index 34ce366cfc6..6bb4db946b3 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/k401.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/k401.yaml @@ -17,6 +17,7 @@ brackets: 2023-01-01: 7_500 2024-01-01: 7_500 2025-01-01: 7_500 + 2026-01-01: 8_000 metadata: uprating: parameter: gov.irs.uprating @@ -35,6 +36,7 @@ brackets: # SECURE 2.0 enhanced catch-up begins in 2025 # The greater of $10,000 or 150% of the standard catch-up 2025-01-01: 11_250 + 2026-01-01: 11_250 metadata: uprating: parameter: gov.irs.uprating @@ -51,6 +53,7 @@ brackets: 2023-01-01: 7_500 2024-01-01: 7_500 2025-01-01: 7_500 + 2026-01-01: 8_000 metadata: uprating: parameter: gov.irs.uprating @@ -70,5 +73,7 @@ metadata: href: https://www.law.cornell.edu/uscode/text/26/414#v_2_E - title: IRS announcement - 401(k) limit increases for 2025 href: https://www.irs.gov/newsroom/401k-limit-increases-to-23500-for-2025-ira-limit-remains-7000 + - title: IRS announcement - 401(k) limit increases for 2026 + href: https://www.irs.gov/newsroom/401k-limit-increases-to-24500-for-2026-ira-limit-increases-to-7500 - title: Cost-of-Living Adjustments for Retirement Items href: https://www.irs.gov/pub/irs-tege/cola-table.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/ut/tax/property/homeowner_renter_relief/nontaxable_income/sources.yaml b/policyengine_us/parameters/gov/states/ut/tax/property/homeowner_renter_relief/nontaxable_income/sources.yaml index 787b963a101..106311a6762 100644 --- a/policyengine_us/parameters/gov/states/ut/tax/property/homeowner_renter_relief/nontaxable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/ut/tax/property/homeowner_renter_relief/nontaxable_income/sources.yaml @@ -8,8 +8,8 @@ values: - tax_exempt_interest_income - tax_exempt_pension_income - tax_exempt_retirement_distributions - - traditional_401k_contributions - - traditional_403b_contributions + - capped_traditional_401k_contributions + - capped_traditional_403b_contributions - capped_traditional_ira_contributions - self_employed_pension_contributions - veterans_benefits diff --git a/policyengine_us/tests/core/test_payroll_contributions.py b/policyengine_us/tests/core/test_payroll_contributions.py index df4908f004f..602f958ecce 100644 --- a/policyengine_us/tests/core/test_payroll_contributions.py +++ b/policyengine_us/tests/core/test_payroll_contributions.py @@ -48,7 +48,7 @@ def make_simulation( PERIOD: pre_tax_health_insurance_premiums }, "tip_income": {PERIOD: tip_income}, - "traditional_401k_contributions": { + "traditional_401k_contributions_reported": { PERIOD: traditional_401k_contributions }, } diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml index 2a68e25c7d3..81bb15a436e 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/above_the_line_deductions/retirement/k401_catch_up_limit.yaml @@ -104,3 +104,24 @@ output: # No enhanced catch-up before 2025 k401_catch_up_limit: 7_500 + +- name: Case 15, age 50 in 2026 receives 2026 standard limit. + period: 2026 + input: + age: 50 + output: + k401_catch_up_limit: 8_000 + +- name: Case 16, age 62 in 2026 receives 2026 enhanced limit. + period: 2026 + input: + age: 62 + output: + k401_catch_up_limit: 11_250 + +- name: Case 17, age 64 in 2026 reverts to 2026 standard limit. + period: 2026 + input: + age: 64 + output: + k401_catch_up_limit: 8_000 diff --git a/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml b/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml new file mode 100644 index 00000000000..05200e3ff2d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/household/expense/retirement/elective_deferral_limit.yaml @@ -0,0 +1,123 @@ +- name: Case 1, reported deferrals below the elective deferral limit. + period: 2025 + input: + age: 35 + traditional_401k_contributions: 12_000 + roth_401k_contributions: 8_000 + output: + elective_deferral_limit: 23_500 + traditional_401k_contributions: 12_000 + roth_401k_contributions: 8_000 + capped_traditional_401k_contributions: 12_000 + capped_roth_401k_contributions: 8_000 + +- name: Case 2, legacy deferral inputs above the base elective deferral limit. + period: 2025 + absolute_error_margin: 0.01 + input: + age: 35 + traditional_401k_contributions: 20_000 + roth_401k_contributions: 10_000 + output: + elective_deferral_limit: 23_500 + traditional_401k_contributions: 20_000 + roth_401k_contributions: 10_000 + capped_traditional_401k_contributions: 15_666.67 + capped_roth_401k_contributions: 7_833.33 + pre_tax_contributions: 15_666.67 + +- name: Case 3, reported inputs use the enhanced catch-up limit across 401(k) and 403(b) deferrals. + period: 2025 + absolute_error_margin: 0.01 + input: + age: 62 + traditional_401k_contributions_reported: 20_000 + roth_401k_contributions_reported: 10_000 + traditional_403b_contributions_reported: 5_000 + roth_403b_contributions_reported: 5_000 + output: + elective_deferral_limit: 34_750 + capped_traditional_401k_contributions: 17_375 + capped_roth_401k_contributions: 8_687.50 + capped_traditional_403b_contributions: 4_343.75 + capped_roth_403b_contributions: 4_343.75 + +- name: Case 4, SECURE 2.0 enhanced catch-up applies from ages 60 through 63. + period: 2025 + input: + people: + person1: + age: 49 + person2: + age: 50 + person3: + age: 59 + person4: + age: 60 + person5: + age: 63 + person6: + age: 64 + output: + elective_deferral_limit: [23_500, 31_000, 31_000, 34_750, 34_750, 31_000] + +- name: Case 5, capped traditional deferrals reduce income tax wages. + period: 2025 + input: + age: 40 + employment_income: 100_000 + traditional_401k_contributions: 50_000 + output: + traditional_401k_contributions: 50_000 + capped_traditional_401k_contributions: 23_500 + pre_tax_contributions: 23_500 + irs_employment_income: 76_500 + +- name: Case 6, capped traditional deferrals do not reduce FICA wages. + period: 2025 + input: + age: 40 + employment_income: 100_000 + traditional_401k_contributions: 50_000 + output: + capped_traditional_401k_contributions: 23_500 + payroll_tax_gross_wages: 100_000 + +- name: Case 7, capped Roth deferrals count for the saver's credit. + period: 2025 + input: + age: 40 + traditional_401k_contributions: 30_000 + roth_401k_contributions: 17_000 + retirement_distributions: 0 + output: + capped_traditional_401k_contributions: 15_000 + capped_roth_401k_contributions: 8_500 + savers_credit_qualified_contributions: 23_500 + +- name: Case 8, the 2026 standard catch-up applies from ages 50 through 59. + period: 2026 + input: + age: 55 + traditional_401k_contributions: 32_500 + output: + elective_deferral_limit: 32_500 + capped_traditional_401k_contributions: 32_500 + +- name: Case 9, the SECURE 2.0 enhanced catch-up applies in 2026. + period: 2026 + input: + age: 62 + traditional_401k_contributions: 35_750 + output: + elective_deferral_limit: 35_750 + capped_traditional_401k_contributions: 35_750 + +- name: Case 10, age 64 returns to the standard catch-up in 2026. + period: 2026 + input: + age: 64 + traditional_401k_contributions: 35_750 + output: + elective_deferral_limit: 32_500 + capped_traditional_401k_contributions: 32_500 diff --git a/policyengine_us/tests/policy/contrib/crfb/agi_surtax.yaml b/policyengine_us/tests/policy/contrib/crfb/agi_surtax.yaml index 928652a50e2..aef65611374 100644 --- a/policyengine_us/tests/policy/contrib/crfb/agi_surtax.yaml +++ b/policyengine_us/tests/policy/contrib/crfb/agi_surtax.yaml @@ -79,9 +79,9 @@ traditional_401k_contributions: 30_000 filing_status: SINGLE output: - # AGI 80k + 401k 30k = 110k expanded base - # (110k - 100k) * 1% = 100 - agi_surtax: 100 + # AGI 80k + capped 401k 24.5k = 104.5k expanded base + # (104.5k - 100k) * 1% = 45 + agi_surtax: 45 - name: Expanded base with tax-exempt interest period: 2026 diff --git a/policyengine_us/variables/household/expense/retirement/capped_roth_401k_contributions.py b/policyengine_us/variables/household/expense/retirement/capped_roth_401k_contributions.py new file mode 100644 index 00000000000..7d8350b5c29 --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/capped_roth_401k_contributions.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class capped_roth_401k_contributions(Variable): + value_type = float + entity = Person + label = "Capped Roth 401(k) contributions" + unit = USD + documentation = ( + "Roth 401(k) contributions after applying the combined 401(k) and " + "403(b) elective deferral limit." + ) + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/402#g" + + def formula(person, period, parameters): + raw = person("uncapped_roth_401k_contributions", period) + total_reported = add( + person, + period, + [ + "uncapped_traditional_401k_contributions", + "uncapped_roth_401k_contributions", + "uncapped_traditional_403b_contributions", + "uncapped_roth_403b_contributions", + ], + ) + scale = min_( + person("elective_deferral_limit", period) / max_(total_reported, 1), 1 + ) + return raw * scale diff --git a/policyengine_us/variables/household/expense/retirement/capped_roth_403b_contributions.py b/policyengine_us/variables/household/expense/retirement/capped_roth_403b_contributions.py new file mode 100644 index 00000000000..1e58c1fc0df --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/capped_roth_403b_contributions.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class capped_roth_403b_contributions(Variable): + value_type = float + entity = Person + label = "Capped Roth 403(b) contributions" + unit = USD + documentation = ( + "Roth 403(b) contributions after applying the combined 401(k) and " + "403(b) elective deferral limit." + ) + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/402#g" + + def formula(person, period, parameters): + raw = person("uncapped_roth_403b_contributions", period) + total_reported = add( + person, + period, + [ + "uncapped_traditional_401k_contributions", + "uncapped_roth_401k_contributions", + "uncapped_traditional_403b_contributions", + "uncapped_roth_403b_contributions", + ], + ) + scale = min_( + person("elective_deferral_limit", period) / max_(total_reported, 1), 1 + ) + return raw * scale diff --git a/policyengine_us/variables/household/expense/retirement/capped_traditional_401k_contributions.py b/policyengine_us/variables/household/expense/retirement/capped_traditional_401k_contributions.py new file mode 100644 index 00000000000..dfd51b4e63e --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/capped_traditional_401k_contributions.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class capped_traditional_401k_contributions(Variable): + value_type = float + entity = Person + label = "Capped traditional 401(k) contributions" + unit = USD + documentation = ( + "Traditional 401(k) contributions after applying the combined " + "401(k) and 403(b) elective deferral limit." + ) + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/402#g" + + def formula(person, period, parameters): + raw = person("uncapped_traditional_401k_contributions", period) + total_reported = add( + person, + period, + [ + "uncapped_traditional_401k_contributions", + "uncapped_roth_401k_contributions", + "uncapped_traditional_403b_contributions", + "uncapped_roth_403b_contributions", + ], + ) + scale = min_( + person("elective_deferral_limit", period) / max_(total_reported, 1), 1 + ) + return raw * scale diff --git a/policyengine_us/variables/household/expense/retirement/capped_traditional_403b_contributions.py b/policyengine_us/variables/household/expense/retirement/capped_traditional_403b_contributions.py new file mode 100644 index 00000000000..755166f5064 --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/capped_traditional_403b_contributions.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class capped_traditional_403b_contributions(Variable): + value_type = float + entity = Person + label = "Capped traditional 403(b) contributions" + unit = USD + documentation = ( + "Traditional 403(b) contributions after applying the combined " + "401(k) and 403(b) elective deferral limit." + ) + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/26/402#g" + + def formula(person, period, parameters): + raw = person("uncapped_traditional_403b_contributions", period) + total_reported = add( + person, + period, + [ + "uncapped_traditional_401k_contributions", + "uncapped_roth_401k_contributions", + "uncapped_traditional_403b_contributions", + "uncapped_roth_403b_contributions", + ], + ) + scale = min_( + person("elective_deferral_limit", period) / max_(total_reported, 1), 1 + ) + return raw * scale diff --git a/policyengine_us/variables/household/expense/retirement/elective_deferral_limit.py b/policyengine_us/variables/household/expense/retirement/elective_deferral_limit.py new file mode 100644 index 00000000000..b7776e86eaf --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/elective_deferral_limit.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class elective_deferral_limit(Variable): + value_type = float + entity = Person + label = "401(k) and 403(b) elective deferral limit" + unit = USD + definition_period = YEAR + reference = ( + "https://www.law.cornell.edu/uscode/text/26/402#g", + "https://www.law.cornell.edu/uscode/text/26/414#v", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.irs.gross_income.retirement_contributions + base_limit = getattr(p.limit, "401k") + catch_up_limit = person("k401_catch_up_limit", period) + return base_limit + catch_up_limit diff --git a/policyengine_us/variables/household/expense/retirement/roth_401k_contributions.py b/policyengine_us/variables/household/expense/retirement/roth_401k_contributions.py index de8f3acec7c..65f84937180 100644 --- a/policyengine_us/variables/household/expense/retirement/roth_401k_contributions.py +++ b/policyengine_us/variables/household/expense/retirement/roth_401k_contributions.py @@ -6,5 +6,5 @@ class roth_401k_contributions(Variable): entity = Person label = "Roth 401(k) contributions" unit = USD - documentation = "Contributions to Roth 401(k) accounts." + documentation = "Reported contributions to Roth 401(k) accounts." definition_period = YEAR diff --git a/policyengine_us/variables/household/expense/retirement/roth_401k_contributions_reported.py b/policyengine_us/variables/household/expense/retirement/roth_401k_contributions_reported.py new file mode 100644 index 00000000000..13c6271e343 --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/roth_401k_contributions_reported.py @@ -0,0 +1,10 @@ +from policyengine_us.model_api import * + + +class roth_401k_contributions_reported(Variable): + value_type = float + entity = Person + label = "Reported Roth 401(k) contributions" + unit = USD + documentation = "Contributions reported to Roth 401(k) accounts before statutory contribution limits." + definition_period = YEAR diff --git a/policyengine_us/variables/household/expense/retirement/roth_403b_contributions.py b/policyengine_us/variables/household/expense/retirement/roth_403b_contributions.py index 071de981620..6db80fb0779 100644 --- a/policyengine_us/variables/household/expense/retirement/roth_403b_contributions.py +++ b/policyengine_us/variables/household/expense/retirement/roth_403b_contributions.py @@ -6,5 +6,5 @@ class roth_403b_contributions(Variable): entity = Person label = "Roth 403(b) contributions" unit = USD - documentation = "Contributions to Roth 403(b) accounts" + documentation = "Reported contributions to Roth 403(b) accounts." definition_period = YEAR diff --git a/policyengine_us/variables/household/expense/retirement/roth_403b_contributions_reported.py b/policyengine_us/variables/household/expense/retirement/roth_403b_contributions_reported.py new file mode 100644 index 00000000000..9109a428a85 --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/roth_403b_contributions_reported.py @@ -0,0 +1,10 @@ +from policyengine_us.model_api import * + + +class roth_403b_contributions_reported(Variable): + value_type = float + entity = Person + label = "Reported Roth 403(b) contributions" + unit = USD + documentation = "Contributions reported to Roth 403(b) accounts before statutory contribution limits." + definition_period = YEAR diff --git a/policyengine_us/variables/household/expense/retirement/traditional_401k_contributions.py b/policyengine_us/variables/household/expense/retirement/traditional_401k_contributions.py index c4ad795132a..2085c9bc39f 100644 --- a/policyengine_us/variables/household/expense/retirement/traditional_401k_contributions.py +++ b/policyengine_us/variables/household/expense/retirement/traditional_401k_contributions.py @@ -6,5 +6,5 @@ class traditional_401k_contributions(Variable): entity = Person label = "Traditional 401(k) contributions" unit = USD - documentation = "Contributions to traditional 401(k) accounts." + documentation = "Reported contributions to traditional 401(k) accounts." definition_period = YEAR diff --git a/policyengine_us/variables/household/expense/retirement/traditional_401k_contributions_reported.py b/policyengine_us/variables/household/expense/retirement/traditional_401k_contributions_reported.py new file mode 100644 index 00000000000..340a13e9c90 --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/traditional_401k_contributions_reported.py @@ -0,0 +1,10 @@ +from policyengine_us.model_api import * + + +class traditional_401k_contributions_reported(Variable): + value_type = float + entity = Person + label = "Reported traditional 401(k) contributions" + unit = USD + documentation = "Contributions reported to traditional 401(k) accounts before statutory contribution limits." + definition_period = YEAR diff --git a/policyengine_us/variables/household/expense/retirement/traditional_403b_contributions.py b/policyengine_us/variables/household/expense/retirement/traditional_403b_contributions.py index 011ea3f55b5..d948442f151 100644 --- a/policyengine_us/variables/household/expense/retirement/traditional_403b_contributions.py +++ b/policyengine_us/variables/household/expense/retirement/traditional_403b_contributions.py @@ -6,5 +6,5 @@ class traditional_403b_contributions(Variable): entity = Person label = "Traditional 403(b) contributions" unit = USD - documentation = "Contributions to traditional 403(b) accounts." + documentation = "Reported contributions to traditional 403(b) accounts." definition_period = YEAR diff --git a/policyengine_us/variables/household/expense/retirement/traditional_403b_contributions_reported.py b/policyengine_us/variables/household/expense/retirement/traditional_403b_contributions_reported.py new file mode 100644 index 00000000000..6824c1b8d5b --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/traditional_403b_contributions_reported.py @@ -0,0 +1,10 @@ +from policyengine_us.model_api import * + + +class traditional_403b_contributions_reported(Variable): + value_type = float + entity = Person + label = "Reported traditional 403(b) contributions" + unit = USD + documentation = "Contributions reported to traditional 403(b) accounts before statutory contribution limits." + definition_period = YEAR diff --git a/policyengine_us/variables/household/expense/retirement/uncapped_roth_401k_contributions.py b/policyengine_us/variables/household/expense/retirement/uncapped_roth_401k_contributions.py new file mode 100644 index 00000000000..8a28bd47ba4 --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/uncapped_roth_401k_contributions.py @@ -0,0 +1,14 @@ +from policyengine_us.model_api import * + + +class uncapped_roth_401k_contributions(Variable): + value_type = float + entity = Person + label = "Uncapped Roth 401(k) contributions" + unit = USD + definition_period = YEAR + + def formula(person, period, parameters): + reported = person("roth_401k_contributions_reported", period) + legacy_reported = person("roth_401k_contributions", period) + return where(reported > 0, reported, legacy_reported) diff --git a/policyengine_us/variables/household/expense/retirement/uncapped_roth_403b_contributions.py b/policyengine_us/variables/household/expense/retirement/uncapped_roth_403b_contributions.py new file mode 100644 index 00000000000..2858c5d1ccc --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/uncapped_roth_403b_contributions.py @@ -0,0 +1,14 @@ +from policyengine_us.model_api import * + + +class uncapped_roth_403b_contributions(Variable): + value_type = float + entity = Person + label = "Uncapped Roth 403(b) contributions" + unit = USD + definition_period = YEAR + + def formula(person, period, parameters): + reported = person("roth_403b_contributions_reported", period) + legacy_reported = person("roth_403b_contributions", period) + return where(reported > 0, reported, legacy_reported) diff --git a/policyengine_us/variables/household/expense/retirement/uncapped_traditional_401k_contributions.py b/policyengine_us/variables/household/expense/retirement/uncapped_traditional_401k_contributions.py new file mode 100644 index 00000000000..4a3fde46e1a --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/uncapped_traditional_401k_contributions.py @@ -0,0 +1,14 @@ +from policyengine_us.model_api import * + + +class uncapped_traditional_401k_contributions(Variable): + value_type = float + entity = Person + label = "Uncapped traditional 401(k) contributions" + unit = USD + definition_period = YEAR + + def formula(person, period, parameters): + reported = person("traditional_401k_contributions_reported", period) + legacy_reported = person("traditional_401k_contributions", period) + return where(reported > 0, reported, legacy_reported) diff --git a/policyengine_us/variables/household/expense/retirement/uncapped_traditional_403b_contributions.py b/policyengine_us/variables/household/expense/retirement/uncapped_traditional_403b_contributions.py new file mode 100644 index 00000000000..48a9a73292e --- /dev/null +++ b/policyengine_us/variables/household/expense/retirement/uncapped_traditional_403b_contributions.py @@ -0,0 +1,14 @@ +from policyengine_us.model_api import * + + +class uncapped_traditional_403b_contributions(Variable): + value_type = float + entity = Person + label = "Uncapped traditional 403(b) contributions" + unit = USD + definition_period = YEAR + + def formula(person, period, parameters): + reported = person("traditional_403b_contributions_reported", period) + legacy_reported = person("traditional_403b_contributions", period) + return where(reported > 0, reported, legacy_reported)