Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ make documentation
- **Variable Naming**: Use snake_case for variable names and function names; use UPPER_CASE for constants
- **Error Handling**: Use np.divide with out/where parameters to avoid divide-by-zero errors
- **Documentation**: Add docstrings to classes and functions; include description, parameters, returns
- **Parameter Access**: Always use `p = parameters(period).gov.<program>` pattern and call parameters as `p.*` to make parameter tree origin clear
- **Parameter Access**: Always use `p = parameters(period).gov.<program>` pattern and call parameters as `p.*` to make parameter tree origin clear. Do not assign parameters to intermediate Python variables (e.g., avoid `rate = p.rate`; instead use `p.rate` directly in expressions)
- **Constants**: Use UPPERCASE only for constants defined in code, not for parameters from the parameter tree
- **Income Combination**: Use `add(person, period, ["income1", "income2"])` instead of manual addition for combining income sources
- **Negative Values**: Use `max_(value, 0)` to clip negative values to zero (prevents counterintuitive behavior in economic models)
Expand Down
4 changes: 4 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- bump: minor
changes:
added:
- Implement SECURE 2.0 enhanced 401(k) catch-up contributions for ages 60-63 (starting 2025).
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
description: Maximum age for enhanced 401(k) catch-up contributions under SECURE 2.0 (inclusive).
values:
# Effective for taxable years beginning after December 31, 2024
2025-01-01: 63
metadata:
unit: year
period: year
label: Enhanced catch-up maximum age
reference:
- title: IRC § 414(v)(2)(E)(ii) - Applicable age range
href: https://www.law.cornell.edu/uscode/text/26/414#v_2_E
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
description: Minimum age for enhanced 401(k) catch-up contributions under SECURE 2.0.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Active voice in all descriptions

values:
# Effective for taxable years beginning after December 31, 2024
2025-01-01: 60
metadata:
unit: year
period: year
label: Enhanced catch-up minimum age
reference:
- title: IRC § 414(v)(2)(E)(ii) - Applicable age range
href: https://www.law.cornell.edu/uscode/text/26/414#v_2_E
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not just make this a scale parameter with min and max age?

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
description: Enhanced 401(k) catch-up contribution limit for individuals aged 60-63, introduced by SECURE 2.0 Act.
values:
# Before 2025, enhanced catch-up did not exist, so we use the standard limit
2018-01-01: 6_000
2020-01-01: 6_500
2023-01-01: 7_500
2024-01-01: 7_500
# SECURE 2.0 enhanced catch-up begins in 2025 (150% of standard base)
2025-01-01: 11_250
metadata:
unit: currency-USD
period: year
label: 401(k) enhanced catch-up amount (ages 60-63)
uprating:
parameter: gov.irs.uprating
rounding:
type: downwards
interval: 500
reference:
- title: IRC § 414(v)(2)(E) - Increased catch-up contribution limit for individuals aged 60-63
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: Federal Register - Catch-up Contributions Final Regulations
href: https://www.federalregister.gov/documents/2025/09/16/2025-17865/catch-up-contributions
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Tests for 401(k) catch-up contribution limits
# Standard catch-up: $7,500 for ages 50+ (2024-2025)
# Enhanced catch-up: $11,250 for ages 60-63 (2025+)

- name: No catch-up limit for age under 50
period: 2025
input:
age: 45
output:
k401_catch_up_limit: 0

- name: Standard catch-up limit for age 50
period: 2025
input:
age: 50
output:
k401_catch_up_limit: 7_500

- name: Standard catch-up limit for age 55
period: 2025
input:
age: 55
output:
k401_catch_up_limit: 7_500

- name: Standard catch-up limit for age 59
period: 2025
input:
age: 59
output:
k401_catch_up_limit: 7_500

- name: Enhanced catch-up limit for age 60 (SECURE 2.0)
period: 2025
input:
age: 60
output:
k401_catch_up_limit: 11_250

- name: Enhanced catch-up limit for age 62 (SECURE 2.0)
period: 2025
input:
age: 62
output:
k401_catch_up_limit: 11_250

- name: Enhanced catch-up limit for age 63 (SECURE 2.0)
period: 2025
input:
age: 63
output:
k401_catch_up_limit: 11_250

- name: Standard catch-up limit for age 64 (past enhanced window)
period: 2025
input:
age: 64
output:
k401_catch_up_limit: 7_500

- name: Standard catch-up limit for age 70
period: 2025
input:
age: 70
output:
k401_catch_up_limit: 7_500

# Test 2024 (before SECURE 2.0 enhanced catch-up)
- name: No enhanced catch-up in 2024 for age 60
period: 2024
input:
age: 60
output:
k401_catch_up_limit: 7_500
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from policyengine_us.model_api import *


class k401_catch_up_limit(Variable):
value_type = float
entity = Person
label = "401(k) catch-up contribution limit"
unit = USD
documentation = (
"Maximum additional 401(k) catch-up contribution for individuals "
"age 50 or older. SECURE 2.0 enhanced limit applies for ages 60-63."
)
definition_period = YEAR
reference = [
"https://www.law.cornell.edu/cfr/text/26/1.414(v)-1",
"https://www.law.cornell.edu/uscode/text/26/414#v_2_E",
]

def formula(person, period, parameters):
age = person("age", period)
p = parameters(
period
).gov.irs.gross_income.retirement_contributions.catch_up

# Standard catch-up eligibility (age 50+)
catch_up_eligible = age >= p.age_threshold

# Enhanced catch-up eligibility (ages 60-63, starting 2025)
enhanced_eligible = (age >= p.enhanced_age_min) & (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make a separate variable for eligibility and used defined for here

age <= p.enhanced_age_max
)

# Apply enhanced limit for ages 60-63, standard for others 50+
return where(
enhanced_eligible,
p.limit.k401_enhanced,
where(catch_up_eligible, p.limit.k401, 0),
)