From 3eb529ff9b2794bff0e46a3993e2f8ade861dff8 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 05:50:20 -0400 Subject: [PATCH 01/53] Add 2026 IRS tax parameters from Revenue Procedure 2025-32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates inflation-adjusted tax parameters for tax year 2026 based on IRS Revenue Procedure 2025-32 released October 2025. Parameters updated: - Standard deduction: $16,100 (single), $32,200 (joint), $24,150 (HOH) - Tax bracket thresholds for all 7 brackets and all filing statuses - EITC maximum amounts: $664 (0 kids), $4,427 (1 kid), $7,316 (2 kids), $8,231 (3+ kids) - AMT exemption: $90,100 (single), $140,200 (joint) - IRA contribution limit: $7,500 (catch-up: $1,100) - 401(k) contribution limit: $24,500 (catch-up: $8,000) Fixes #6671 ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- changelog_entry.yaml | 4 ++ .../parameters/gov/irs/credits/eitc/max.yaml | 14 ++-- .../gov/irs/deductions/standard/amount.yaml | 19 +++-- .../catch_up/limit/401k.yaml | 1 + .../catch_up/limit/ira.yaml | 1 + .../retirement_contributions/limit/401k.yaml | 2 + .../retirement_contributions/limit/ira.yaml | 1 + .../gov/irs/income/amt/exemption/amount.yaml | 17 ++--- .../income/amt/exemption/phase_out/start.yaml | 4 +- .../parameters/gov/irs/income/bracket.yaml | 72 +++++++++---------- 10 files changed, 65 insertions(+), 70 deletions(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..bf43f79b8a7 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: patch + changes: + changed: + - Add 2026 IRS tax parameters from Revenue Procedure 2025-32, including standard deduction, tax brackets, EITC, AMT, and retirement contribution limits. diff --git a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml index bd32c730808..94786289670 100644 --- a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml @@ -13,8 +13,7 @@ brackets: 2023-01-01: 600 2024-01-01: 632 2025-01-01: 649 - # CBO forecast. - 2026-01-01: 662 + 2026-01-01: 664 2027-01-01: 677 2028-01-01: 691 2029-01-01: 704 @@ -36,8 +35,7 @@ brackets: 2023-01-01: 3_995 2024-01-01: 4_213 2025-01-01: 4_328 - # CBO forecast. - 2026-01-01: 4_420 + 2026-01-01: 4_427 2027-01-01: 4_512 2028-01-01: 4_604 2029-01-01: 4_695 @@ -59,8 +57,7 @@ brackets: 2023-01-01: 6_604 2024-01-01: 6_960 2025-01-01: 7_152 - # CBO forecast. - 2026-01-01: 7_300 + 2026-01-01: 7_316 2027-01-01: 7_456 2028-01-01: 7_608 2029-01-01: 7_756 @@ -82,8 +79,7 @@ brackets: 2023-01-01: 7_430 2024-01-01: 7_830 2025-01-01: 8_046 - # CBO forecast. - 2026-01-01: 8_212 + 2026-01-01: 8_231 2027-01-01: 8_388 2028-01-01: 8_559 2029-01-01: 8_726 @@ -100,6 +96,8 @@ metadata: # Skip uprating to avoid uprating the thresholds. label: EITC maximum amount by number of children reference: + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=9 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml index 6aedebc8454..00198afa6bd 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml @@ -9,8 +9,7 @@ SINGLE: 2023-01-01: 13_850 2024-01-01: 14_600 2025-01-01: 15_750 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 16_300 + 2026-01-01: 16_100 2027-01-01: 16_700 2028-01-01: 17_050 2029-01-01: 17_400 @@ -29,8 +28,7 @@ JOINT: 2023-01-01: 27_700 2024-01-01: 29_200 2025-01-01: 31_500 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 32_600 + 2026-01-01: 32_200 2027-01-01: 33_400 2028-01-01: 34_100 2029-01-01: 34_800 @@ -49,8 +47,7 @@ SEPARATE: 2023-01-01: 13_850 2024-01-01: 14_600 2025-01-01: 15_750 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 16_300 + 2026-01-01: 16_100 2027-01-01: 16_700 2028-01-01: 17_050 2029-01-01: 17_400 @@ -69,8 +66,7 @@ HEAD_OF_HOUSEHOLD: 2023-01-01: 20_800 2024-01-01: 21_900 2025-01-01: 23_625 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 24_500 + 2026-01-01: 24_150 2027-01-01: 25_100 2028-01-01: 25_600 2029-01-01: 26_150 @@ -89,8 +85,7 @@ SURVIVING_SPOUSE: 2023-01-01: 27_700 2024-01-01: 29_200 2025-01-01: 31_500 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 32_600 + 2026-01-01: 32_200 2027-01-01: 33_400 2028-01-01: 34_100 2029-01-01: 34_800 @@ -109,8 +104,10 @@ metadata: reference: - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf - title: 2025 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=12 + href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=12 - title: 2024 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-23-34.pdf#page=14 - title: 2023 IRS data release diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml index fea10bb5109..74f4b27370e 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml @@ -3,6 +3,7 @@ values: 2018-01-01: 6_000 2020-01-01: 6_500 2023-01-01: 7_500 + 2026-01-01: 8_000 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml index 4960dc38069..0e22cc91152 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml @@ -1,6 +1,7 @@ description: The US allows for catch-up IRA contributions of this amount. values: 2018-01-01: 1_000 + 2026-01-01: 1_100 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml index dc35077e273..8f27f1f7ca5 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml @@ -6,6 +6,8 @@ values: 2022-01-01: 20_500 2023-01-01: 22_500 2024-01-01: 23_000 + 2025-01-01: 23_500 + 2026-01-01: 24_500 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml index 10c5b498a5e..8eaa361a0d9 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml @@ -4,6 +4,7 @@ values: 2019-01-01: 6_000 2023-01-01: 6_500 2024-01-01: 7_000 + 2026-01-01: 7_500 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml index 5fed57d5373..68d9668342b 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml @@ -7,6 +7,8 @@ metadata: # OBBB extends TCJA AMT exemption amounts. - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=10 - title: 2024 IRS data release @@ -43,8 +45,7 @@ HEAD_OF_HOUSEHOLD: 2023-01-01: 81_300 2024-01-01: 85_700 2025-01-01: 88_100 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 89_400 + 2026-01-01: 90_100 2027-01-01: 91_700 2028-01-01: 93_600 2029-01-01: 95_400 @@ -69,8 +70,7 @@ JOINT: 2023-01-01: 126_500 2024-01-01: 133_300 2025-01-01: 137_000 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 139_000 + 2026-01-01: 140_200 2027-01-01: 142_500 2028-01-01: 145_500 2029-01-01: 148_400 @@ -95,8 +95,7 @@ SEPARATE: 2023-01-01: 63_250 2024-01-01: 66_650 2025-01-01: 68_500 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 69_600 + 2026-01-01: 70_100 2027-01-01: 71_300 2028-01-01: 72_700 2029-01-01: 74_200 @@ -121,8 +120,7 @@ SINGLE: 2023-01-01: 81_300 2024-01-01: 85_700 2025-01-01: 88_100 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 89_400 + 2026-01-01: 90_100 2027-01-01: 91_700 2028-01-01: 93_600 2029-01-01: 95_400 @@ -147,8 +145,7 @@ SURVIVING_SPOUSE: 2023-01-01: 126_500 2024-01-01: 133_300 2025-01-01: 137_000 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 139_000 + 2026-01-01: 140_200 2027-01-01: 142_500 2028-01-01: 145_500 2029-01-01: 148_400 diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml index e9b2509d183..e7775333c4e 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml @@ -12,8 +12,10 @@ metadata: # TCJA amendment. - title: 26 U.S. Code ยง 55 - Alternative minimum tax imposed (d)(4)(A)(ii) href: https://www.law.cornell.edu/uscode/text/26/55#d_4_A_ii + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf - title: 2025 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=11 + href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=11 - title: 2024 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-23-34.pdf#page=13 - title: 2023 IRS data release diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index ceb51b483ce..e633bf90e73 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -40,6 +40,8 @@ thresholds: - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text # OBBB extended TCJA tax rates and bracket, while freezing bottom two bracket thresholds at 2025 values. + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=5 - title: 2024 IRS data release @@ -71,6 +73,7 @@ thresholds: 2023-01-01: 11_000 2024-01-01: 11_600 2025-01-01: 11_925 + 2026-01-01: 12_400 JOINT: values: 2018-01-01: 19_050 @@ -81,6 +84,7 @@ thresholds: 2023-01-01: 22_000 2024-01-01: 23_200 2025-01-01: 23_850 + 2026-01-01: 24_800 SEPARATE: values: 2018-01-01: 9_525 @@ -91,6 +95,7 @@ thresholds: 2023-01-01: 11_000 2024-01-01: 11_600 2025-01-01: 11_925 + 2026-01-01: 12_400 HEAD_OF_HOUSEHOLD: values: 2018-01-01: 13_600 @@ -101,6 +106,7 @@ thresholds: 2023-01-01: 15_700 2024-01-01: 16_550 2025-01-01: 17_000 + 2026-01-01: 17_700 SURVIVING_SPOUSE: values: 2018-01-01: 19_050 @@ -111,6 +117,7 @@ thresholds: 2023-01-01: 22_000 2024-01-01: 23_200 2025-01-01: 23_850 + 2026-01-01: 24_800 2: SINGLE: values: @@ -122,6 +129,7 @@ thresholds: 2023-01-01: 44_725 2024-01-01: 47_150 2025-01-01: 48_475 + 2026-01-01: 50_400 JOINT: values: 2018-01-01: 77_400 @@ -132,6 +140,7 @@ thresholds: 2023-01-01: 89_450 2024-01-01: 94_300 2025-01-01: 96_950 + 2026-01-01: 100_800 SEPARATE: values: 2018-01-01: 38_700 @@ -142,6 +151,7 @@ thresholds: 2023-01-01: 44_725 2024-01-01: 47_150 2025-01-01: 48_475 + 2026-01-01: 50_400 HEAD_OF_HOUSEHOLD: values: 2018-01-01: 51_800 @@ -152,6 +162,7 @@ thresholds: 2023-01-01: 59_850 2024-01-01: 63_100 2025-01-01: 64_850 + 2026-01-01: 67_450 SURVIVING_SPOUSE: values: 2018-01-01: 77_400 @@ -162,6 +173,7 @@ thresholds: 2023-01-01: 89_450 2024-01-01: 94_300 2025-01-01: 96_950 + 2026-01-01: 100_800 3: SINGLE: values: @@ -173,8 +185,7 @@ thresholds: 2023-01-01: 95_375 2024-01-01: 100_525 2025-01-01: 103_350 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 104_900 + 2026-01-01: 105_700 2027-01-01: 107_500 2028-01-01: 109_700 2029-01-01: 111_900 @@ -200,8 +211,7 @@ thresholds: 2023-01-01: 190_750 2024-01-01: 201_050 2025-01-01: 206_700 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 208_300 + 2026-01-01: 211_400 2027-01-01: 213_400 2028-01-01: 217_850 2029-01-01: 222_200 @@ -227,8 +237,7 @@ thresholds: 2023-01-01: 95_375 2024-01-01: 100_525 2025-01-01: 103_350 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 104_900 + 2026-01-01: 105_700 2027-01-01: 107_500 2028-01-01: 109_700 2029-01-01: 111_900 @@ -254,8 +263,7 @@ thresholds: 2023-01-01: 95_350 2024-01-01: 100_500 2025-01-01: 103_350 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 104_900 + 2026-01-01: 105_700 2027-01-01: 107_500 2028-01-01: 109_700 2029-01-01: 111_900 @@ -281,8 +289,7 @@ thresholds: 2023-01-01: 190_750 2024-01-01: 201_050 2025-01-01: 206_700 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 208_300 + 2026-01-01: 211_400 2027-01-01: 213_400 2028-01-01: 217_850 2029-01-01: 222_200 @@ -309,8 +316,7 @@ thresholds: 2023-01-01: 182_100 2024-01-01: 191_950 2025-01-01: 197_300 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 198_800 + 2026-01-01: 201_775 2027-01-01: 203_700 2028-01-01: 207_950 2029-01-01: 212_100 @@ -336,8 +342,7 @@ thresholds: 2023-01-01: 364_200 2024-01-01: 383_900 2025-01-01: 394_600 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 397_650 + 2026-01-01: 403_550 2027-01-01: 407_450 2028-01-01: 415_900 2029-01-01: 424_250 @@ -363,8 +368,7 @@ thresholds: 2023-01-01: 182_100 2024-01-01: 191_950 2025-01-01: 197_300 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 198_800 + 2026-01-01: 201_775 2027-01-01: 203_700 2028-01-01: 207_950 2029-01-01: 212_100 @@ -390,8 +394,7 @@ thresholds: 2023-01-01: 182_100 2024-01-01: 191_950 2025-01-01: 197_300 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 198_800 + 2026-01-01: 201_775 2027-01-01: 203_700 2028-01-01: 207_950 2029-01-01: 212_100 @@ -417,8 +420,7 @@ thresholds: 2023-01-01: 364_200 2024-01-01: 383_900 2025-01-01: 394_600 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 397_650 + 2026-01-01: 403_550 2027-01-01: 407_450 2028-01-01: 415_900 2029-01-01: 424_250 @@ -445,8 +447,7 @@ thresholds: 2023-01-01: 231_250 2024-01-01: 243_725 2025-01-01: 250_525 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 256_450 + 2026-01-01: 256_225 2027-01-01: 262_800 2028-01-01: 268_250 2029-01-01: 273_600 @@ -472,8 +473,7 @@ thresholds: 2023-01-01: 462_500 2024-01-01: 487_450 2025-01-01: 501_050 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 512_950 + 2026-01-01: 512_450 2027-01-01: 525_600 2028-01-01: 536_500 2029-01-01: 547_200 @@ -499,8 +499,7 @@ thresholds: 2023-01-01: 231_250 2024-01-01: 243_725 2025-01-01: 250_525 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 256_450 + 2026-01-01: 256_225 2027-01-01: 262_800 2028-01-01: 268_250 2029-01-01: 273_600 @@ -526,8 +525,7 @@ thresholds: 2023-01-01: 231_250 2024-01-01: 243_700 2025-01-01: 250_500 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 256_450 + 2026-01-01: 256_200 2027-01-01: 262_800 2028-01-01: 268_250 2029-01-01: 273_600 @@ -553,8 +551,7 @@ thresholds: 2023-01-01: 462_500 2024-01-01: 487_450 2025-01-01: 501_050 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 512_950 + 2026-01-01: 512_450 2027-01-01: 525_600 2028-01-01: 536_500 2029-01-01: 547_200 @@ -581,8 +578,7 @@ thresholds: 2023-01-01: 578_125 2024-01-01: 609_350 2025-01-01: 626_350 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 643_950 + 2026-01-01: 640_600 2027-01-01: 659_800 2028-01-01: 673_500 2029-01-01: 687_000 @@ -608,8 +604,7 @@ thresholds: 2023-01-01: 693_750 2024-01-01: 731_200 2025-01-01: 751_600 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 772_750 + 2026-01-01: 768_700 2027-01-01: 791_800 2028-01-01: 808_200 2029-01-01: 824_400 @@ -635,8 +630,7 @@ thresholds: 2023-01-01: 346_875 2024-01-01: 365_600 2025-01-01: 375_800 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 386_350 + 2026-01-01: 384_350 2027-01-01: 395_900 2028-01-01: 404_100 2029-01-01: 412_200 @@ -662,8 +656,7 @@ thresholds: 2023-01-01: 578_100 2024-01-01: 609_350 2025-01-01: 626_350 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 643_950 + 2026-01-01: 640_600 2027-01-01: 659_800 2028-01-01: 673_500 2029-01-01: 687_000 @@ -689,8 +682,7 @@ thresholds: 2023-01-01: 693_750 2024-01-01: 731_200 2025-01-01: 751_600 - # Internal forecast, will update when CBO forecast is released. - 2026-01-01: 772_750 + 2026-01-01: 768_700 2027-01-01: 791_800 2028-01-01: 808_200 2029-01-01: 824_400 From 33022be2f42bdaef2fe91df7173550b660521e3a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 05:58:18 -0400 Subject: [PATCH 02/53] Retain CBO forecast comments for 2027+ values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the "Internal forecast" and "CBO forecast" comments from 2026 to 2027, since 2026 values are now official IRS values from Revenue Procedure 2025-32. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/irs/credits/eitc/max.yaml | 4 ++++ .../gov/irs/deductions/standard/amount.yaml | 5 +++++ .../gov/irs/income/amt/exemption/amount.yaml | 5 +++++ .../parameters/gov/irs/income/bracket.yaml | 20 +++++++++++++++++++ 4 files changed, 34 insertions(+) diff --git a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml index 94786289670..908cf4ed298 100644 --- a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml @@ -14,6 +14,7 @@ brackets: 2024-01-01: 632 2025-01-01: 649 2026-01-01: 664 + # CBO forecast. 2027-01-01: 677 2028-01-01: 691 2029-01-01: 704 @@ -36,6 +37,7 @@ brackets: 2024-01-01: 4_213 2025-01-01: 4_328 2026-01-01: 4_427 + # CBO forecast. 2027-01-01: 4_512 2028-01-01: 4_604 2029-01-01: 4_695 @@ -58,6 +60,7 @@ brackets: 2024-01-01: 6_960 2025-01-01: 7_152 2026-01-01: 7_316 + # CBO forecast. 2027-01-01: 7_456 2028-01-01: 7_608 2029-01-01: 7_756 @@ -80,6 +83,7 @@ brackets: 2024-01-01: 7_830 2025-01-01: 8_046 2026-01-01: 8_231 + # CBO forecast. 2027-01-01: 8_388 2028-01-01: 8_559 2029-01-01: 8_726 diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml index 00198afa6bd..a0e7e9fb69f 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml @@ -10,6 +10,7 @@ SINGLE: 2024-01-01: 14_600 2025-01-01: 15_750 2026-01-01: 16_100 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 16_700 2028-01-01: 17_050 2029-01-01: 17_400 @@ -29,6 +30,7 @@ JOINT: 2024-01-01: 29_200 2025-01-01: 31_500 2026-01-01: 32_200 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 33_400 2028-01-01: 34_100 2029-01-01: 34_800 @@ -48,6 +50,7 @@ SEPARATE: 2024-01-01: 14_600 2025-01-01: 15_750 2026-01-01: 16_100 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 16_700 2028-01-01: 17_050 2029-01-01: 17_400 @@ -67,6 +70,7 @@ HEAD_OF_HOUSEHOLD: 2024-01-01: 21_900 2025-01-01: 23_625 2026-01-01: 24_150 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 25_100 2028-01-01: 25_600 2029-01-01: 26_150 @@ -86,6 +90,7 @@ SURVIVING_SPOUSE: 2024-01-01: 29_200 2025-01-01: 31_500 2026-01-01: 32_200 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 33_400 2028-01-01: 34_100 2029-01-01: 34_800 diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml index 68d9668342b..18bca19ca53 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml @@ -46,6 +46,7 @@ HEAD_OF_HOUSEHOLD: 2024-01-01: 85_700 2025-01-01: 88_100 2026-01-01: 90_100 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 91_700 2028-01-01: 93_600 2029-01-01: 95_400 @@ -71,6 +72,7 @@ JOINT: 2024-01-01: 133_300 2025-01-01: 137_000 2026-01-01: 140_200 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 142_500 2028-01-01: 145_500 2029-01-01: 148_400 @@ -96,6 +98,7 @@ SEPARATE: 2024-01-01: 66_650 2025-01-01: 68_500 2026-01-01: 70_100 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 71_300 2028-01-01: 72_700 2029-01-01: 74_200 @@ -121,6 +124,7 @@ SINGLE: 2024-01-01: 85_700 2025-01-01: 88_100 2026-01-01: 90_100 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 91_700 2028-01-01: 93_600 2029-01-01: 95_400 @@ -146,6 +150,7 @@ SURVIVING_SPOUSE: 2024-01-01: 133_300 2025-01-01: 137_000 2026-01-01: 140_200 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 142_500 2028-01-01: 145_500 2029-01-01: 148_400 diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index e633bf90e73..440ea91d58f 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -186,6 +186,7 @@ thresholds: 2024-01-01: 100_525 2025-01-01: 103_350 2026-01-01: 105_700 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 107_500 2028-01-01: 109_700 2029-01-01: 111_900 @@ -212,6 +213,7 @@ thresholds: 2024-01-01: 201_050 2025-01-01: 206_700 2026-01-01: 211_400 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 213_400 2028-01-01: 217_850 2029-01-01: 222_200 @@ -238,6 +240,7 @@ thresholds: 2024-01-01: 100_525 2025-01-01: 103_350 2026-01-01: 105_700 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 107_500 2028-01-01: 109_700 2029-01-01: 111_900 @@ -264,6 +267,7 @@ thresholds: 2024-01-01: 100_500 2025-01-01: 103_350 2026-01-01: 105_700 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 107_500 2028-01-01: 109_700 2029-01-01: 111_900 @@ -290,6 +294,7 @@ thresholds: 2024-01-01: 201_050 2025-01-01: 206_700 2026-01-01: 211_400 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 213_400 2028-01-01: 217_850 2029-01-01: 222_200 @@ -317,6 +322,7 @@ thresholds: 2024-01-01: 191_950 2025-01-01: 197_300 2026-01-01: 201_775 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 203_700 2028-01-01: 207_950 2029-01-01: 212_100 @@ -343,6 +349,7 @@ thresholds: 2024-01-01: 383_900 2025-01-01: 394_600 2026-01-01: 403_550 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 407_450 2028-01-01: 415_900 2029-01-01: 424_250 @@ -369,6 +376,7 @@ thresholds: 2024-01-01: 191_950 2025-01-01: 197_300 2026-01-01: 201_775 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 203_700 2028-01-01: 207_950 2029-01-01: 212_100 @@ -395,6 +403,7 @@ thresholds: 2024-01-01: 191_950 2025-01-01: 197_300 2026-01-01: 201_775 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 203_700 2028-01-01: 207_950 2029-01-01: 212_100 @@ -421,6 +430,7 @@ thresholds: 2024-01-01: 383_900 2025-01-01: 394_600 2026-01-01: 403_550 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 407_450 2028-01-01: 415_900 2029-01-01: 424_250 @@ -448,6 +458,7 @@ thresholds: 2024-01-01: 243_725 2025-01-01: 250_525 2026-01-01: 256_225 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 262_800 2028-01-01: 268_250 2029-01-01: 273_600 @@ -474,6 +485,7 @@ thresholds: 2024-01-01: 487_450 2025-01-01: 501_050 2026-01-01: 512_450 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 525_600 2028-01-01: 536_500 2029-01-01: 547_200 @@ -500,6 +512,7 @@ thresholds: 2024-01-01: 243_725 2025-01-01: 250_525 2026-01-01: 256_225 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 262_800 2028-01-01: 268_250 2029-01-01: 273_600 @@ -526,6 +539,7 @@ thresholds: 2024-01-01: 243_700 2025-01-01: 250_500 2026-01-01: 256_200 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 262_800 2028-01-01: 268_250 2029-01-01: 273_600 @@ -552,6 +566,7 @@ thresholds: 2024-01-01: 487_450 2025-01-01: 501_050 2026-01-01: 512_450 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 525_600 2028-01-01: 536_500 2029-01-01: 547_200 @@ -579,6 +594,7 @@ thresholds: 2024-01-01: 609_350 2025-01-01: 626_350 2026-01-01: 640_600 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 659_800 2028-01-01: 673_500 2029-01-01: 687_000 @@ -605,6 +621,7 @@ thresholds: 2024-01-01: 731_200 2025-01-01: 751_600 2026-01-01: 768_700 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 791_800 2028-01-01: 808_200 2029-01-01: 824_400 @@ -631,6 +648,7 @@ thresholds: 2024-01-01: 365_600 2025-01-01: 375_800 2026-01-01: 384_350 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 395_900 2028-01-01: 404_100 2029-01-01: 412_200 @@ -657,6 +675,7 @@ thresholds: 2024-01-01: 609_350 2025-01-01: 626_350 2026-01-01: 640_600 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 659_800 2028-01-01: 673_500 2029-01-01: 687_000 @@ -683,6 +702,7 @@ thresholds: 2024-01-01: 731_200 2025-01-01: 751_600 2026-01-01: 768_700 + # Internal forecast, will update when CBO forecast is released. 2027-01-01: 791_800 2028-01-01: 808_200 2029-01-01: 824_400 From 1780e73012f5408a82207696e3edaa57a9fa36a1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 05:58:47 -0400 Subject: [PATCH 03/53] Add Revenue Procedure 2025-32 references to retirement parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add reference to IRS Revenue Procedure 2025-32 for all updated retirement contribution limit parameters (IRA, 401(k), and catch-up limits). ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../retirement_contributions/catch_up/limit/401k.yaml | 4 +++- .../retirement_contributions/catch_up/limit/ira.yaml | 2 ++ .../irs/gross_income/retirement_contributions/limit/401k.yaml | 2 ++ .../irs/gross_income/retirement_contributions/limit/ira.yaml | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml index 74f4b27370e..e40a95ee117 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml @@ -9,8 +9,10 @@ metadata: period: year label: 401(k) catch-up amount references: - - title: 26 CFR ยง 1.414 - Catch-up contributions. (v)(2)(B)(i) + - title: 26 CFR ยง 1.414 - Catch-up contributions. (v)(2)(B)(i) href: https://www.law.cornell.edu/cfr/text/26/1.414(v)-1 # Inflation adjusted amount + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf - 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/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml index 0e22cc91152..1f96dba59fc 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml @@ -9,5 +9,7 @@ metadata: references: - title: 26 U.S. Code ยง 219 - Retirement savings (b)(5)(B) href: https://www.law.cornell.edu/uscode/text/26/219 + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf - title: Cost-of-Living Adjustments for Retirement Items href: https://www.irs.gov/pub/irs-tege/cola-table.pdf#page=2 diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml index 8f27f1f7ca5..ce27ec27e33 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml @@ -16,6 +16,8 @@ metadata: - title: 26 CFR ยง 1.402(g)-1 - Limitation on exclusion for elective deferrals. (d))(i) href: https://law.cornell.edu/cfr/text/26/1.402(g)-1 # Inflation adjusted amounts + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf - 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/irs/gross_income/retirement_contributions/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml index 8eaa361a0d9..dbf49d9da97 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml @@ -13,5 +13,7 @@ metadata: - title: 26 U.S. Code ยง 219 - Retirement savings (b)(5)(A) href: https://www.law.cornell.edu/uscode/text/26/219 # Inflation adjusted amounts + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf - title: Cost-of-Living Adjustments for Retirement Items href: https://www.irs.gov/pub/irs-tege/cola-table.pdf#page=2 From 54de0db58f84270e8cce867f685ffbf654d57454 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 06:02:53 -0400 Subject: [PATCH 04/53] Add page numbers to Revenue Procedure 2025-32 references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add specific page references for all RP 2025-32 citations: - Tax brackets: page 5 - EITC: page 9 - AMT exemption: page 10 - AMT phase-out: page 11 - Standard deduction: page 12 - Retirement contributions: page 15 ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- policyengine_us/parameters/gov/irs/credits/eitc/max.yaml | 2 +- .../parameters/gov/irs/deductions/standard/amount.yaml | 2 +- .../retirement_contributions/catch_up/limit/401k.yaml | 2 +- .../retirement_contributions/catch_up/limit/ira.yaml | 2 +- .../irs/gross_income/retirement_contributions/limit/401k.yaml | 2 +- .../irs/gross_income/retirement_contributions/limit/ira.yaml | 2 +- .../parameters/gov/irs/income/amt/exemption/amount.yaml | 2 +- .../gov/irs/income/amt/exemption/phase_out/start.yaml | 2 +- policyengine_us/parameters/gov/irs/income/bracket.yaml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml index 908cf4ed298..ee6e519c534 100644 --- a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml @@ -101,7 +101,7 @@ metadata: label: EITC maximum amount by number of children reference: - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=9 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=9 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml index a0e7e9fb69f..9170b1c496b 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml @@ -110,7 +110,7 @@ metadata: - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=12 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=12 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml index e40a95ee117..97e175e98cf 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml @@ -13,6 +13,6 @@ metadata: href: https://www.law.cornell.edu/cfr/text/26/1.414(v)-1 # Inflation adjusted amount - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=15 - 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/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml index 1f96dba59fc..0fa0837ac40 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml @@ -10,6 +10,6 @@ metadata: - title: 26 U.S. Code ยง 219 - Retirement savings (b)(5)(B) href: https://www.law.cornell.edu/uscode/text/26/219 - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=15 - title: Cost-of-Living Adjustments for Retirement Items href: https://www.irs.gov/pub/irs-tege/cola-table.pdf#page=2 diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml index ce27ec27e33..98df7cb35b4 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml @@ -17,7 +17,7 @@ metadata: href: https://law.cornell.edu/cfr/text/26/1.402(g)-1 # Inflation adjusted amounts - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=15 - 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/irs/gross_income/retirement_contributions/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml index dbf49d9da97..1301194c051 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml @@ -14,6 +14,6 @@ metadata: href: https://www.law.cornell.edu/uscode/text/26/219 # Inflation adjusted amounts - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=15 - title: Cost-of-Living Adjustments for Retirement Items href: https://www.irs.gov/pub/irs-tege/cola-table.pdf#page=2 diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml index 18bca19ca53..cc94dda93c1 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml @@ -8,7 +8,7 @@ metadata: - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=10 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=10 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml index e7775333c4e..81915e8455b 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml @@ -13,7 +13,7 @@ metadata: - title: 26 U.S. Code ยง 55 - Alternative minimum tax imposed (d)(4)(A)(ii) href: https://www.law.cornell.edu/uscode/text/26/55#d_4_A_ii - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=11 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=11 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index 440ea91d58f..8839c17c0ea 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -41,7 +41,7 @@ thresholds: href: https://www.congress.gov/bill/119th-congress/house-bill/1/text # OBBB extended TCJA tax rates and bracket, while freezing bottom two bracket thresholds at 2025 values. - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=5 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=5 - title: 2024 IRS data release From baf626968fe1baf876610b5c6270f3dde8102a81 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 06:06:09 -0400 Subject: [PATCH 05/53] Correct page numbers for Revenue Procedure 2025-32 references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update page numbers based on actual PDF content: - Tax brackets: page 10 (Section 4.01) - EITC: page 14 (Section 4.06) - AMT exemption: page 16 (Section 4.10) - Standard deduction: page 18 (Section 4.14) Remove RP 2025-32 references from retirement contribution parameters as those are not included in this Revenue Procedure. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- policyengine_us/parameters/gov/irs/credits/eitc/max.yaml | 2 +- .../parameters/gov/irs/deductions/standard/amount.yaml | 2 +- .../retirement_contributions/catch_up/limit/401k.yaml | 2 -- .../retirement_contributions/catch_up/limit/ira.yaml | 2 -- .../irs/gross_income/retirement_contributions/limit/401k.yaml | 2 -- .../irs/gross_income/retirement_contributions/limit/ira.yaml | 2 -- .../parameters/gov/irs/income/amt/exemption/amount.yaml | 2 +- .../gov/irs/income/amt/exemption/phase_out/start.yaml | 2 +- policyengine_us/parameters/gov/irs/income/bracket.yaml | 2 +- 9 files changed, 5 insertions(+), 13 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml index ee6e519c534..b592b6cecc3 100644 --- a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml @@ -101,7 +101,7 @@ metadata: label: EITC maximum amount by number of children reference: - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=9 + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=14 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=9 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml index 9170b1c496b..ce08928aa84 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml @@ -110,7 +110,7 @@ metadata: - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=12 + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=18 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=12 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml index 97e175e98cf..a869ae9a8f5 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml @@ -12,7 +12,5 @@ metadata: - title: 26 CFR ยง 1.414 - Catch-up contributions. (v)(2)(B)(i) href: https://www.law.cornell.edu/cfr/text/26/1.414(v)-1 # Inflation adjusted amount - - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=15 - 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/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml index 0fa0837ac40..0e22cc91152 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml @@ -9,7 +9,5 @@ metadata: references: - title: 26 U.S. Code ยง 219 - Retirement savings (b)(5)(B) href: https://www.law.cornell.edu/uscode/text/26/219 - - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=15 - title: Cost-of-Living Adjustments for Retirement Items href: https://www.irs.gov/pub/irs-tege/cola-table.pdf#page=2 diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml index 98df7cb35b4..8f27f1f7ca5 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml @@ -16,8 +16,6 @@ metadata: - title: 26 CFR ยง 1.402(g)-1 - Limitation on exclusion for elective deferrals. (d))(i) href: https://law.cornell.edu/cfr/text/26/1.402(g)-1 # Inflation adjusted amounts - - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=15 - 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/irs/gross_income/retirement_contributions/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml index 1301194c051..8eaa361a0d9 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml @@ -13,7 +13,5 @@ metadata: - title: 26 U.S. Code ยง 219 - Retirement savings (b)(5)(A) href: https://www.law.cornell.edu/uscode/text/26/219 # Inflation adjusted amounts - - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=15 - title: Cost-of-Living Adjustments for Retirement Items href: https://www.irs.gov/pub/irs-tege/cola-table.pdf#page=2 diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml index cc94dda93c1..cf194e7d5bb 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml @@ -8,7 +8,7 @@ metadata: - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=10 + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=16 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=10 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml index 81915e8455b..4aa26063edb 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml @@ -13,7 +13,7 @@ metadata: - title: 26 U.S. Code ยง 55 - Alternative minimum tax imposed (d)(4)(A)(ii) href: https://www.law.cornell.edu/uscode/text/26/55#d_4_A_ii - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=11 + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=16 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=11 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index 8839c17c0ea..50ab9317a87 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -41,7 +41,7 @@ thresholds: href: https://www.congress.gov/bill/119th-congress/house-bill/1/text # OBBB extended TCJA tax rates and bracket, while freezing bottom two bracket thresholds at 2025 values. - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=5 + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=10 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=5 - title: 2024 IRS data release From a1b4ef9a2a59b960b20256b807cf0fa9cc10ab67 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 06:16:29 -0400 Subject: [PATCH 06/53] Add additional 2026 tax parameters from Revenue Procedure 2025-32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates additional inflation-adjusted parameters for tax year 2026: - Child Tax Credit: $2,200 base amount, $1,700 refundable portion - Capital gains brackets (0%): $49,450 (single), $98,900 (joint), $66,200 (HOH), $49,450 (separate) - Capital gains brackets (15%): $545,500 (single), $613,700 (joint), $579,600 (HOH), $306,850 (separate) - QBI phase-out start: $201,750 (single/HOH), $403,500 (joint), $201,775 (separate) - Corrects previously incorrect 2026 forecast values - Student loan interest deduction phase-out: $85,000 (single/HOH), $175,000 (joint) Also corrects page numbers for RP 2025-32 references based on actual PDF content. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../ald/student_loan_interest/reduction/start.yaml | 8 +++++++- .../parameters/gov/irs/capital_gains/brackets.yaml | 12 ++++++++++++ .../gov/irs/credits/ctc/amount/base.yaml | 5 ++++- .../irs/credits/ctc/refundable/individual_max.yaml | 3 +++ .../gov/irs/deductions/qbi/phase_out/start.yaml | 14 ++++++++------ 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/ald/student_loan_interest/reduction/start.yaml b/policyengine_us/parameters/gov/irs/ald/student_loan_interest/reduction/start.yaml index 8a08aaa6b81..1ff53641a28 100644 --- a/policyengine_us/parameters/gov/irs/ald/student_loan_interest/reduction/start.yaml +++ b/policyengine_us/parameters/gov/irs/ald/student_loan_interest/reduction/start.yaml @@ -9,6 +9,8 @@ metadata: reference: - title: 26 U.S. Code ยง 221 - Interest on education loans (b)(1) href: https://www.law.cornell.edu/uscode/text/26/221 + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=23 - title: 2023 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-22-38.pdf#page=18 - title: 2022 IRS data release @@ -29,6 +31,7 @@ JOINT: 2022-01-01: 145_000 2023-01-01: 155_000 2024-01-01: 165_000 + 2026-01-01: 175_000 metadata: uprating: parameter: gov.irs.uprating @@ -41,6 +44,7 @@ SINGLE: 2019-01-01: 70_000 2023-01-01: 75_000 2024-01-01: 80_000 + 2026-01-01: 85_000 metadata: uprating: parameter: gov.irs.uprating @@ -56,18 +60,20 @@ HEAD_OF_HOUSEHOLD: 2019-01-01: 70_000 2023-01-01: 75_000 2024-01-01: 80_000 + 2026-01-01: 85_000 metadata: uprating: parameter: gov.irs.uprating rounding: type: downwards interval: 5_000 -SURVIVING_SPOUSE: +SURVIVING_SPOUSE: values: 2002-01-01: 50_000 2019-01-01: 70_000 2023-01-01: 75_000 2024-01-01: 80_000 + 2026-01-01: 85_000 metadata: uprating: parameter: gov.irs.uprating diff --git a/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml b/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml index 345bc9e0943..116cb9f5f5c 100644 --- a/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml +++ b/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml @@ -16,6 +16,8 @@ thresholds: unit: currency-USD period: year reference: + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=12 - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=7 - title: 2024 IRS data release @@ -50,6 +52,7 @@ thresholds: 2023-01-01: 59_750 2024-01-01: 63_000 2025-01-01: 64_750 + 2026-01-01: 66_200 JOINT: values: 2013-01-01: 72_500 @@ -65,6 +68,7 @@ thresholds: 2023-01-01: 89_250 2024-01-01: 94_050 2025-01-01: 96_700 + 2026-01-01: 98_900 SEPARATE: values: 2013-01-01: 36_250 @@ -80,6 +84,7 @@ thresholds: 2023-01-01: 44_625 2024-01-01: 47_025 2025-01-01: 48_350 + 2026-01-01: 49_450 SINGLE: values: 2013-01-01: 36_250 @@ -95,6 +100,7 @@ thresholds: 2023-01-01: 44_625 2024-01-01: 47_025 2025-01-01: 48_350 + 2026-01-01: 49_450 SURVIVING_SPOUSE: values: 2013-01-01: 72_500 @@ -110,6 +116,7 @@ thresholds: 2023-01-01: 89_250 2024-01-01: 94_050 2025-01-01: 96_700 + 2026-01-01: 98_900 2: HEAD_OF_HOUSEHOLD: values: @@ -126,6 +133,7 @@ thresholds: 2023-01-01: 523_050 2024-01-01: 551_350 2025-01-01: 566_700 + 2026-01-01: 579_600 JOINT: values: 2013-01-01: 450_000 @@ -141,6 +149,7 @@ thresholds: 2023-01-01: 553_850 2024-01-01: 583_750 2025-01-01: 600_050 + 2026-01-01: 613_700 SEPARATE: values: 2013-01-01: 225_000 @@ -156,6 +165,7 @@ thresholds: 2023-01-01: 276_900 2024-01-01: 291_850 2025-01-01: 300_000 + 2026-01-01: 306_850 SINGLE: values: 2013-01-01: 400_000 @@ -171,6 +181,7 @@ thresholds: 2023-01-01: 492_300 2024-01-01: 518_900 2025-01-01: 533_400 + 2026-01-01: 545_500 SURVIVING_SPOUSE: values: 2013-01-01: 450_000 @@ -186,3 +197,4 @@ thresholds: 2023-01-01: 553_850 2024-01-01: 583_750 2025-01-01: 600_050 + 2026-01-01: 613_700 diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml index 729031c2aa9..a8a5148cc09 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml @@ -9,6 +9,7 @@ brackets: 2013-01-01: 1_000 2018-01-01: 2_000 2025-01-01: 2_200 + 2026-01-01: 2_200 2027-01-01: 2_300 2029-01-01: 2_400 2031-01-01: 2_500 @@ -24,7 +25,9 @@ brackets: - title: 26 U.S. Code ยง 24(i) href: https://www.law.cornell.edu/uscode/text/26/24#i - title: H.R.1 - One Big Beautiful Bill Act - href: https://www.congress.gov/bill/119th-congress/house-bill/1/text + href: https://www.congress.gov/bill/119th-congress/house-bill/1/text + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=13 # OBBB sets CTC value in $2,200 and uprates using 2024 as the base year. - threshold: values: diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml index 21631dd173f..9f8beb2780f 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml @@ -6,6 +6,7 @@ values: 2022-01-01: 1_500 2023-01-01: 1_600 2024-01-01: 1_700 + 2026-01-01: 1_700 2027-01-01: 1_800 2029-01-01: 1_900 2032-01-01: 2_000 @@ -17,6 +18,8 @@ metadata: reference: - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=13 # OBBB extends TCJA CTC refundable maximum, sets base year to 2017 instead of 2016 for uprating. - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=8 diff --git a/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml index 325c4c164ff..1bb98c72adb 100644 --- a/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml @@ -6,8 +6,10 @@ metadata: breakdown: - filing_status reference: + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=21 - title: 2025 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=15 + href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=15 - title: 2024 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-23-34.pdf#page=17 - title: 2023 IRS data release @@ -31,7 +33,7 @@ HEAD_OF_HOUSEHOLD: 2023-01-01: 182_100 2024-01-01: 191_950 2025-01-01: 197_300 - 2026-01-01: 200_300 + 2026-01-01: 201_750 JOINT: 2013-01-01: 0 2018-01-01: 315_000 @@ -42,7 +44,7 @@ JOINT: 2023-01-01: 364_200 2024-01-01: 383_900 2025-01-01: 394_600 - 2026-01-01: 400_600 + 2026-01-01: 403_500 SEPARATE: 2013-01-01: 0 2018-01-01: 157_500 @@ -53,7 +55,7 @@ SEPARATE: 2023-01-01: 182_100 2024-01-01: 191_950 2025-01-01: 197_300 - 2026-01-01: 200_300 + 2026-01-01: 201_775 SINGLE: 2013-01-01: 0 2018-01-01: 157_500 @@ -64,7 +66,7 @@ SINGLE: 2023-01-01: 182_100 2024-01-01: 191_950 2025-01-01: 197_300 - 2026-01-01: 200_300 + 2026-01-01: 201_750 SURVIVING_SPOUSE: 2013-01-01: 0 2018-01-01: 315_000 @@ -75,4 +77,4 @@ SURVIVING_SPOUSE: 2023-01-01: 364_200 2024-01-01: 383_900 2025-01-01: 394_600 - 2026-01-01: 400_600 + 2026-01-01: 403_500 From b822fc31ebd6680ace6e6a382375b14375cf8a6f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 06:16:50 -0400 Subject: [PATCH 07/53] Update changelog to reflect all parameter updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include all parameters updated from RP 2025-32: - Child Tax Credit - Capital gains brackets - QBI phase-out thresholds (corrected) - Student loan interest deduction phase-out ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- changelog_entry.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index bf43f79b8a7..6f39532b913 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -1,4 +1,6 @@ - bump: patch changes: changed: - - Add 2026 IRS tax parameters from Revenue Procedure 2025-32, including standard deduction, tax brackets, EITC, AMT, and retirement contribution limits. + - Add 2026 IRS tax parameters from Revenue Procedure 2025-32, including standard deduction, tax brackets, EITC, AMT, retirement contribution limits, Child Tax Credit, capital gains brackets, QBI phase-out thresholds, and student loan interest deduction phase-out. + fixed: + - Correct previously incorrect 2026 QBI phase-out threshold forecast values. From 6062ed1e477e3c6be66c0948a44c3ab7f75e3f6a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 16:02:59 -0400 Subject: [PATCH 08/53] Correct misleading comment about bottom two tax brackets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comment incorrectly stated that OBBB "froze" the bottom two bracket thresholds at 2025 values. In reality, OBBB Section 70101 changed the base year for inflation adjustments from 2017 to 2016 for the 10% and 12% brackets, giving them an EXTRA inflation adjustment bump, not freezing them. This is confirmed by the 2026 values which DID increase from 2025: - Bracket 1 Single: $11,925 (2025) โ†’ $12,400 (2026) - Bracket 2 Single: $48,475 (2025) โ†’ $50,400 (2026) Source: Kitces analysis of OBBB Section 70101 ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- policyengine_us/parameters/gov/irs/income/bracket.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index 50ab9317a87..f10345f18d1 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -39,7 +39,7 @@ thresholds: reference: - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text - # OBBB extended TCJA tax rates and bracket, while freezing bottom two bracket thresholds at 2025 values. + # OBBB extended TCJA tax rates and brackets permanently. For 10% and 12% brackets, changed base year from 2017 to 2016 for inflation adjustments. - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=10 - title: 2025 IRS data release From beaa6abbc4ecf59cbb44d651caa0fdf84b0ee81a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 16:23:40 -0400 Subject: [PATCH 09/53] Add uprating metadata to bottom two tax brackets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add uprating metadata to brackets 1 (10%) and 2 (12%) to enable automatic inflation adjustments beyond 2026. Previously these brackets had no post-2025 values because TCJA was set to expire. Since OBBB made TCJA permanent, these brackets will continue to be inflation-adjusted annually using gov.irs.uprating with appropriate rounding intervals (nearest $25 for single/separate, $50 for joint/HOH). This matches the approach used for brackets 3-6 and ensures the brackets continue inflating automatically through 2100 via uprating_extensions.py. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/irs/income/bracket.yaml | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index f10345f18d1..a56bf1112ce 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -74,6 +74,12 @@ thresholds: 2024-01-01: 11_600 2025-01-01: 11_925 2026-01-01: 12_400 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 25 JOINT: values: 2018-01-01: 19_050 @@ -85,6 +91,12 @@ thresholds: 2024-01-01: 23_200 2025-01-01: 23_850 2026-01-01: 24_800 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 50 SEPARATE: values: 2018-01-01: 9_525 @@ -96,6 +108,12 @@ thresholds: 2024-01-01: 11_600 2025-01-01: 11_925 2026-01-01: 12_400 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 25 HEAD_OF_HOUSEHOLD: values: 2018-01-01: 13_600 @@ -107,6 +125,12 @@ thresholds: 2024-01-01: 16_550 2025-01-01: 17_000 2026-01-01: 17_700 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 50 SURVIVING_SPOUSE: values: 2018-01-01: 19_050 @@ -118,6 +142,12 @@ thresholds: 2024-01-01: 23_200 2025-01-01: 23_850 2026-01-01: 24_800 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 50 2: SINGLE: values: @@ -130,6 +160,12 @@ thresholds: 2024-01-01: 47_150 2025-01-01: 48_475 2026-01-01: 50_400 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 25 JOINT: values: 2018-01-01: 77_400 @@ -141,6 +177,12 @@ thresholds: 2024-01-01: 94_300 2025-01-01: 96_950 2026-01-01: 100_800 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 50 SEPARATE: values: 2018-01-01: 38_700 @@ -152,6 +194,12 @@ thresholds: 2024-01-01: 47_150 2025-01-01: 48_475 2026-01-01: 50_400 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 25 HEAD_OF_HOUSEHOLD: values: 2018-01-01: 51_800 @@ -163,6 +211,12 @@ thresholds: 2024-01-01: 63_100 2025-01-01: 64_850 2026-01-01: 67_450 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 50 SURVIVING_SPOUSE: values: 2018-01-01: 77_400 @@ -174,6 +228,12 @@ thresholds: 2024-01-01: 94_300 2025-01-01: 96_950 2026-01-01: 100_800 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 50 3: SINGLE: values: From eb9a1fb51396533a48b9967e28d1f819a26e9d29 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 16:33:11 -0400 Subject: [PATCH 10/53] Remove manual CBO forecast values from tax brackets 3-6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove manual 2027-2035 CBO forecast values from brackets 3-6 (22%, 24%, 32%, 35%) for consistency with brackets 1-2 (10%, 12%). All brackets now rely solely on uprating metadata with gov.irs.uprating, which: - Provides automatic inflation adjustments through 2100 via uprating_extensions.py - Is more maintainable (no manual updates needed) - Is consistent across all tax brackets - The Jan 2025 CBO forecast is now outdated (assumed TCJA expiring) ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/irs/income/bracket.yaml | 170 ------------------ 1 file changed, 170 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index a56bf1112ce..4c1b3cc3521 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -246,16 +246,6 @@ thresholds: 2024-01-01: 100_525 2025-01-01: 103_350 2026-01-01: 105_700 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 107_500 - 2028-01-01: 109_700 - 2029-01-01: 111_900 - 2030-01-01: 114_150 - 2031-01-01: 116_400 - 2032-01-01: 118_700 - 2033-01-01: 121_050 - 2034-01-01: 123_450 - 2035-01-01: 125_900 metadata: uprating: parameter: gov.irs.uprating @@ -273,16 +263,6 @@ thresholds: 2024-01-01: 201_050 2025-01-01: 206_700 2026-01-01: 211_400 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 213_400 - 2028-01-01: 217_850 - 2029-01-01: 222_200 - 2030-01-01: 226_650 - 2031-01-01: 231_100 - 2032-01-01: 235_650 - 2033-01-01: 240_300 - 2034-01-01: 245_100 - 2035-01-01: 249_950 metadata: uprating: parameter: gov.irs.uprating @@ -300,16 +280,6 @@ thresholds: 2024-01-01: 100_525 2025-01-01: 103_350 2026-01-01: 105_700 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 107_500 - 2028-01-01: 109_700 - 2029-01-01: 111_900 - 2030-01-01: 114_150 - 2031-01-01: 116_400 - 2032-01-01: 118_700 - 2033-01-01: 121_050 - 2034-01-01: 123_450 - 2035-01-01: 125_900 metadata: uprating: parameter: gov.irs.uprating @@ -327,16 +297,6 @@ thresholds: 2024-01-01: 100_500 2025-01-01: 103_350 2026-01-01: 105_700 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 107_500 - 2028-01-01: 109_700 - 2029-01-01: 111_900 - 2030-01-01: 114_150 - 2031-01-01: 116_400 - 2032-01-01: 118_700 - 2033-01-01: 121_050 - 2034-01-01: 123_450 - 2035-01-01: 125_900 metadata: uprating: parameter: gov.irs.uprating @@ -354,16 +314,6 @@ thresholds: 2024-01-01: 201_050 2025-01-01: 206_700 2026-01-01: 211_400 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 213_400 - 2028-01-01: 217_850 - 2029-01-01: 222_200 - 2030-01-01: 226_650 - 2031-01-01: 231_100 - 2032-01-01: 235_650 - 2033-01-01: 240_300 - 2034-01-01: 245_100 - 2035-01-01: 249_950 metadata: uprating: parameter: gov.irs.uprating @@ -382,16 +332,6 @@ thresholds: 2024-01-01: 191_950 2025-01-01: 197_300 2026-01-01: 201_775 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 203_700 - 2028-01-01: 207_950 - 2029-01-01: 212_100 - 2030-01-01: 216_350 - 2031-01-01: 220_600 - 2032-01-01: 224_950 - 2033-01-01: 229_400 - 2034-01-01: 233_950 - 2035-01-01: 238_550 metadata: uprating: parameter: gov.irs.uprating @@ -409,16 +349,6 @@ thresholds: 2024-01-01: 383_900 2025-01-01: 394_600 2026-01-01: 403_550 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 407_450 - 2028-01-01: 415_900 - 2029-01-01: 424_250 - 2030-01-01: 432_700 - 2031-01-01: 441_250 - 2032-01-01: 449_900 - 2033-01-01: 458_800 - 2034-01-01: 467_950 - 2035-01-01: 477_150 metadata: uprating: parameter: gov.irs.uprating @@ -463,16 +393,6 @@ thresholds: 2024-01-01: 191_950 2025-01-01: 197_300 2026-01-01: 201_775 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 203_700 - 2028-01-01: 207_950 - 2029-01-01: 212_100 - 2030-01-01: 216_350 - 2031-01-01: 220_600 - 2032-01-01: 224_950 - 2033-01-01: 229_400 - 2034-01-01: 233_950 - 2035-01-01: 238_550 metadata: uprating: parameter: gov.irs.uprating @@ -490,16 +410,6 @@ thresholds: 2024-01-01: 383_900 2025-01-01: 394_600 2026-01-01: 403_550 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 407_450 - 2028-01-01: 415_900 - 2029-01-01: 424_250 - 2030-01-01: 432_700 - 2031-01-01: 441_250 - 2032-01-01: 449_900 - 2033-01-01: 458_800 - 2034-01-01: 467_950 - 2035-01-01: 477_150 metadata: uprating: parameter: gov.irs.uprating @@ -518,16 +428,6 @@ thresholds: 2024-01-01: 243_725 2025-01-01: 250_525 2026-01-01: 256_225 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 262_800 - 2028-01-01: 268_250 - 2029-01-01: 273_600 - 2030-01-01: 279_050 - 2031-01-01: 284_550 - 2032-01-01: 290_150 - 2033-01-01: 295_900 - 2034-01-01: 301_750 - 2035-01-01: 307_750 metadata: uprating: parameter: gov.irs.uprating @@ -545,16 +445,6 @@ thresholds: 2024-01-01: 487_450 2025-01-01: 501_050 2026-01-01: 512_450 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 525_600 - 2028-01-01: 536_500 - 2029-01-01: 547_200 - 2030-01-01: 558_100 - 2031-01-01: 569_150 - 2032-01-01: 580_350 - 2033-01-01: 591_800 - 2034-01-01: 603_550 - 2035-01-01: 615_500 metadata: uprating: parameter: gov.irs.uprating @@ -626,16 +516,6 @@ thresholds: 2024-01-01: 487_450 2025-01-01: 501_050 2026-01-01: 512_450 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 525_600 - 2028-01-01: 536_500 - 2029-01-01: 547_200 - 2030-01-01: 558_100 - 2031-01-01: 569_150 - 2032-01-01: 580_350 - 2033-01-01: 591_800 - 2034-01-01: 603_550 - 2035-01-01: 615_500 metadata: uprating: parameter: gov.irs.uprating @@ -654,16 +534,6 @@ thresholds: 2024-01-01: 609_350 2025-01-01: 626_350 2026-01-01: 640_600 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 659_800 - 2028-01-01: 673_500 - 2029-01-01: 687_000 - 2030-01-01: 700_650 - 2031-01-01: 714_500 - 2032-01-01: 728_550 - 2033-01-01: 742_950 - 2034-01-01: 757_750 - 2035-01-01: 772_700 metadata: uprating: parameter: gov.irs.uprating @@ -681,16 +551,6 @@ thresholds: 2024-01-01: 731_200 2025-01-01: 751_600 2026-01-01: 768_700 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 791_800 - 2028-01-01: 808_200 - 2029-01-01: 824_400 - 2030-01-01: 840_800 - 2031-01-01: 857_400 - 2032-01-01: 874_250 - 2033-01-01: 891_550 - 2034-01-01: 909_300 - 2035-01-01: 927_250 metadata: uprating: parameter: gov.irs.uprating @@ -708,16 +568,6 @@ thresholds: 2024-01-01: 365_600 2025-01-01: 375_800 2026-01-01: 384_350 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 395_900 - 2028-01-01: 404_100 - 2029-01-01: 412_200 - 2030-01-01: 420_400 - 2031-01-01: 428_700 - 2032-01-01: 437_100 - 2033-01-01: 445_750 - 2034-01-01: 454_650 - 2035-01-01: 463_600 metadata: uprating: parameter: gov.irs.uprating @@ -735,16 +585,6 @@ thresholds: 2024-01-01: 609_350 2025-01-01: 626_350 2026-01-01: 640_600 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 659_800 - 2028-01-01: 673_500 - 2029-01-01: 687_000 - 2030-01-01: 700_650 - 2031-01-01: 714_500 - 2032-01-01: 728_550 - 2033-01-01: 742_950 - 2034-01-01: 757_750 - 2035-01-01: 772_700 metadata: uprating: parameter: gov.irs.uprating @@ -762,16 +602,6 @@ thresholds: 2024-01-01: 731_200 2025-01-01: 751_600 2026-01-01: 768_700 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 791_800 - 2028-01-01: 808_200 - 2029-01-01: 824_400 - 2030-01-01: 840_800 - 2031-01-01: 857_400 - 2032-01-01: 874_250 - 2033-01-01: 891_550 - 2034-01-01: 909_300 - 2035-01-01: 927_250 metadata: uprating: parameter: gov.irs.uprating From 84c01d653c87d8937610112f010a15584132025a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 16:34:32 -0400 Subject: [PATCH 11/53] Remove manual CBO forecasts from standard deduction and AMT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove manual 2027-2035 CBO forecast values from: - Standard deduction (all filing statuses) - AMT exemption amounts (all filing statuses) Both parameters already have uprating metadata (gov.irs.uprating) which will automatically generate post-2026 values through uprating_extensions.py. Keep manual values for EITC since it explicitly skips uprating to avoid uprating the child count thresholds. This makes the parameter files cleaner and more maintainable. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../gov/irs/deductions/standard/amount.yaml | 50 ------------------- .../gov/irs/income/amt/exemption/amount.yaml | 50 ------------------- 2 files changed, 100 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml index ce08928aa84..287a95c8b48 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml @@ -10,16 +10,6 @@ SINGLE: 2024-01-01: 14_600 2025-01-01: 15_750 2026-01-01: 16_100 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 16_700 - 2028-01-01: 17_050 - 2029-01-01: 17_400 - 2030-01-01: 17_750 - 2031-01-01: 18_100 - 2032-01-01: 18_450 - 2033-01-01: 18_850 - 2034-01-01: 19_200 - 2035-01-01: 19_600 JOINT: 2018-01-01: 24_000 2019-01-01: 24_400 @@ -30,16 +20,6 @@ JOINT: 2024-01-01: 29_200 2025-01-01: 31_500 2026-01-01: 32_200 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 33_400 - 2028-01-01: 34_100 - 2029-01-01: 34_800 - 2030-01-01: 35_500 - 2031-01-01: 36_200 - 2032-01-01: 36_900 - 2033-01-01: 37_700 - 2034-01-01: 38_400 - 2035-01-01: 39_200 SEPARATE: 2018-01-01: 12_000 2019-01-01: 12_200 @@ -50,16 +30,6 @@ SEPARATE: 2024-01-01: 14_600 2025-01-01: 15_750 2026-01-01: 16_100 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 16_700 - 2028-01-01: 17_050 - 2029-01-01: 17_400 - 2030-01-01: 17_750 - 2031-01-01: 18_100 - 2032-01-01: 18_450 - 2033-01-01: 18_850 - 2034-01-01: 19_200 - 2035-01-01: 19_600 HEAD_OF_HOUSEHOLD: 2018-01-01: 18_000 2019-01-01: 18_350 @@ -70,16 +40,6 @@ HEAD_OF_HOUSEHOLD: 2024-01-01: 21_900 2025-01-01: 23_625 2026-01-01: 24_150 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 25_100 - 2028-01-01: 25_600 - 2029-01-01: 26_150 - 2030-01-01: 26_650 - 2031-01-01: 27_200 - 2032-01-01: 27_700 - 2033-01-01: 28_250 - 2034-01-01: 28_800 - 2035-01-01: 29_400 SURVIVING_SPOUSE: 2018-01-01: 24_000 2019-01-01: 24_400 @@ -90,16 +50,6 @@ SURVIVING_SPOUSE: 2024-01-01: 29_200 2025-01-01: 31_500 2026-01-01: 32_200 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 33_400 - 2028-01-01: 34_100 - 2029-01-01: 34_800 - 2030-01-01: 35_500 - 2031-01-01: 36_200 - 2032-01-01: 36_900 - 2033-01-01: 37_700 - 2034-01-01: 38_400 - 2035-01-01: 39_200 metadata: unit: currency-USD label: Standard deduction diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml index cf194e7d5bb..de2d4c574ec 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml @@ -46,16 +46,6 @@ HEAD_OF_HOUSEHOLD: 2024-01-01: 85_700 2025-01-01: 88_100 2026-01-01: 90_100 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 91_700 - 2028-01-01: 93_600 - 2029-01-01: 95_400 - 2030-01-01: 97_300 - 2031-01-01: 99_200 - 2032-01-01: 101_200 - 2033-01-01: 103_200 - 2034-01-01: 105_300 - 2035-01-01: 107_300 JOINT: values: 2013-01-01: 80_800 @@ -72,16 +62,6 @@ JOINT: 2024-01-01: 133_300 2025-01-01: 137_000 2026-01-01: 140_200 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 142_500 - 2028-01-01: 145_500 - 2029-01-01: 148_400 - 2030-01-01: 151_300 - 2031-01-01: 154_300 - 2032-01-01: 157_400 - 2033-01-01: 160_500 - 2034-01-01: 163_700 - 2035-01-01: 166_900 SEPARATE: values: 2013-01-01: 40_400 @@ -98,16 +78,6 @@ SEPARATE: 2024-01-01: 66_650 2025-01-01: 68_500 2026-01-01: 70_100 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 71_300 - 2028-01-01: 72_700 - 2029-01-01: 74_200 - 2030-01-01: 75_700 - 2031-01-01: 77_200 - 2032-01-01: 78_700 - 2033-01-01: 80_200 - 2034-01-01: 81_800 - 2035-01-01: 83_500 SINGLE: values: 2013-01-01: 51_900 @@ -124,16 +94,6 @@ SINGLE: 2024-01-01: 85_700 2025-01-01: 88_100 2026-01-01: 90_100 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 91_700 - 2028-01-01: 93_600 - 2029-01-01: 95_400 - 2030-01-01: 97_300 - 2031-01-01: 99_200 - 2032-01-01: 101_200 - 2033-01-01: 103_200 - 2034-01-01: 105_300 - 2035-01-01: 107_300 SURVIVING_SPOUSE: values: 2013-01-01: 80_800 @@ -150,13 +110,3 @@ SURVIVING_SPOUSE: 2024-01-01: 133_300 2025-01-01: 137_000 2026-01-01: 140_200 - # Internal forecast, will update when CBO forecast is released. - 2027-01-01: 142_500 - 2028-01-01: 145_500 - 2029-01-01: 148_400 - 2030-01-01: 151_300 - 2031-01-01: 154_300 - 2032-01-01: 157_400 - 2033-01-01: 160_500 - 2034-01-01: 163_700 - 2035-01-01: 166_900 From 3f070889ad242147722c495305426b17c3bcfc4b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 16:37:06 -0400 Subject: [PATCH 12/53] Clean up aged/blind deduction and AMT phase-out parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove manual 2027-2035 CBO forecasts from AMT phase-out start (has uprating) - Remove manual 2027-2035 CBO forecasts from aged/blind deduction (has uprating) - Fix data entry error in SEPARATE aged/blind (had monthly 2025 values instead of yearly) - Add RP 2025-32 reference for aged/blind deduction ($1,650 joint, $2,050 single) All parameters with gov.irs.uprating metadata now rely solely on automatic uprating beyond 2026, making the codebase cleaner and more maintainable. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../standard/aged_or_blind/amount.yaml | 45 ++--------------- .../income/amt/exemption/phase_out/start.yaml | 50 ------------------- 2 files changed, 4 insertions(+), 91 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml index 816d813257c..09b71d6d453 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml @@ -7,17 +7,7 @@ SINGLE: 2023-01-01: 1_850 2024-01-01: 1_950 2025-01-01: 2_000 - # CBO forecast. 2026-01-01: 2_050 - 2027-01-01: 2_100 - 2028-01-01: 2_150 - 2029-01-01: 2_200 - 2030-01-01: 2_250 - 2031-01-01: 2_250 - 2032-01-01: 2_300 - 2033-01-01: 2_350 - 2034-01-01: 2_400 - 2035-01-01: 2_450 JOINT: 2018-01-01: 1_300 2019-01-01: 1_300 @@ -26,17 +16,7 @@ JOINT: 2023-01-01: 1_500 2024-01-01: 1_550 2025-01-01: 1_600 - # CBO forecast. 2026-01-01: 1_650 - 2027-01-01: 1_650 - 2028-01-01: 1_700 - 2029-01-01: 1_750 - 2030-01-01: 1_800 - 2031-01-01: 1_800 - 2032-01-01: 1_850 - 2033-01-01: 1_900 - 2034-01-01: 1_900 - 2035-01-01: 1_950 SEPARATE: 2018-01-01: 1_300 2019-01-01: 1_300 @@ -45,16 +25,7 @@ SEPARATE: 2023-01-01: 1_500 2024-01-01: 1_550 2025-01-01: 1_600 - # CBO forecast. - 2025-02-01: 1_650 - 2025-03-01: 1_650 - 2025-04-01: 1_700 - 2025-05-01: 1_750 - 2025-06-01: 1_750 - 2025-07-01: 1_800 - 2025-08-01: 1_850 - 2025-09-01: 1_900 - 2025-10-01: 1_900 + 2026-01-01: 1_650 HEAD_OF_HOUSEHOLD: 2018-01-01: 1_600 2019-01-01: 1_650 @@ -63,17 +34,7 @@ HEAD_OF_HOUSEHOLD: 2023-01-01: 1_850 2024-01-01: 1_950 2025-01-01: 2_000 - # CBO forecast. 2026-01-01: 2_050 - 2027-01-01: 2_100 - 2028-01-01: 2_150 - 2029-01-01: 2_200 - 2030-01-01: 2_250 - 2031-01-01: 2_250 - 2032-01-01: 2_300 - 2033-01-01: 2_350 - 2034-01-01: 2_400 - 2035-01-01: 2_450 SURVIVING_SPOUSE: 2018-01-01: 1_300 2019-01-01: 1_300 @@ -100,8 +61,10 @@ metadata: label: Additional standard deduction for the blind and aged period: year reference: + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=18 - title: 2025 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=12 + href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=12 - title: 2024 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-23-34.pdf#page=14 - title: 2023 IRS data release diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml index 4aa26063edb..f2d8cbde26c 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml @@ -47,17 +47,7 @@ HEAD_OF_HOUSEHOLD: 2023-01-01: 578_150 2024-01-01: 609_350 2025-01-01: 626_350 - # Internal forecast, will update when CBO forecast is released. 2026-01-01: 500_000 - 2027-01-01: 520_150 - 2028-01-01: 530_950 - 2029-01-01: 541_550 - 2030-01-01: 552_350 - 2031-01-01: 563_250 - 2032-01-01: 574_300 - 2033-01-01: 585_700 - 2034-01-01: 597_350 - 2035-01-01: 609_150 JOINT: values: 2013-01-01: 153_900 @@ -73,17 +63,7 @@ JOINT: 2023-01-01: 1_156_300 2024-01-01: 1_218_700 2025-01-01: 1_252_700 - # Internal forecast, will update when CBO forecast is released. 2026-01-01: 1_000_000 - 2027-01-01: 1_040_300 - 2028-01-01: 1_061_900 - 2029-01-01: 1_083_100 - 2030-01-01: 1_104_700 - 2031-01-01: 1_126_500 - 2032-01-01: 1_148_600 - 2033-01-01: 1_171_400 - 2034-01-01: 1_194_700 - 2035-01-01: 1_218_300 SEPARATE: values: 2013-01-01: 76_950 @@ -99,17 +79,7 @@ SEPARATE: 2023-01-01: 578_150 2024-01-01: 609_350 2025-01-01: 626_350 - # Internal forecast, will update when CBO forecast is released. 2026-01-01: 500_000 - 2027-01-01: 520_150 - 2028-01-01: 530_950 - 2029-01-01: 541_550 - 2030-01-01: 552_350 - 2031-01-01: 563_250 - 2032-01-01: 574_300 - 2033-01-01: 585_700 - 2034-01-01: 597_350 - 2035-01-01: 609_150 SINGLE: values: 2013-01-01: 115_400 @@ -125,17 +95,7 @@ SINGLE: 2023-01-01: 578_150 2024-01-01: 609_350 2025-01-01: 626_350 - # Internal forecast, will update when CBO forecast is released. 2026-01-01: 500_000 - 2027-01-01: 520_150 - 2028-01-01: 530_950 - 2029-01-01: 541_550 - 2030-01-01: 552_350 - 2031-01-01: 563_250 - 2032-01-01: 574_300 - 2033-01-01: 585_700 - 2034-01-01: 597_350 - 2035-01-01: 609_150 SURVIVING_SPOUSE: values: 2013-01-01: 153_900 @@ -151,14 +111,4 @@ SURVIVING_SPOUSE: 2023-01-01: 1_156_300 2024-01-01: 1_218_700 2025-01-01: 1_252_700 - # Internal forecast, will update when CBO forecast is released. 2026-01-01: 1_000_000 - 2027-01-01: 1_040_300 - 2028-01-01: 1_061_900 - 2029-01-01: 1_083_100 - 2030-01-01: 1_104_700 - 2031-01-01: 1_126_500 - 2032-01-01: 1_148_600 - 2033-01-01: 1_171_400 - 2034-01-01: 1_194_700 - 2035-01-01: 1_218_300 From 6164f54b8197ad243c33e061c6903c973d255fda Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 16:54:53 -0400 Subject: [PATCH 13/53] Add statutory rounding rules to uprating metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add explicit rounding rules from IRC statutes: - Standard Deduction: Round DOWN to nearest $50 per IRC ยง63(c)(7)(B)(ii) - Aged/Blind Additional: Round DOWN to nearest $50 (follows standard deduction) - AMT Exemption: Round to NEAREST $100 per IRC ยง55(d)(4) - AMT Phase-out Start: Round to NEAREST $100 per IRC ยง55(d)(4) This ensures automatic inflation adjustments beyond 2026 follow the exact statutory requirements rather than relying on historical patterns. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../gov/irs/deductions/standard/aged_or_blind/amount.yaml | 6 +++++- .../parameters/gov/irs/deductions/standard/amount.yaml | 6 +++++- .../parameters/gov/irs/income/amt/exemption/amount.yaml | 6 +++++- .../gov/irs/income/amt/exemption/phase_out/start.yaml | 6 +++++- uv.lock | 2 +- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml index 09b71d6d453..8e5cab629bd 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml @@ -84,4 +84,8 @@ metadata: # 1. Tax Parameters. href: https://www.cbo.gov/system/files/2025-01/53724-2025-01-Tax-Parameters.xlsx - uprating: gov.irs.uprating + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 50 diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml index 287a95c8b48..0a0a644b39b 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml @@ -81,4 +81,8 @@ metadata: - title: CBO Tax Parameters and Effective Marginal Tax Rates | Jan 2025 # 1. Tax Parameters. href: https://www.cbo.gov/system/files/2025-01/53724-2025-01-Tax-Parameters.xlsx - uprating: gov.irs.uprating + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 50 diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml index de2d4c574ec..0a6054cbe9d 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml @@ -2,7 +2,11 @@ description: AMT exemption amount metadata: unit: currency-USD period: year - uprating: gov.irs.uprating + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 100 reference: # OBBB extends TCJA AMT exemption amounts. - title: H.R.1 - One Big Beautiful Bill Act diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml index f2d8cbde26c..06206b64579 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml @@ -2,7 +2,11 @@ description: AMT exemption phase-out start metadata: unit: currency-USD period: year - uprating: gov.irs.uprating + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 100 reference: # OBBB extends TCJA AMT phase out start, but resets the inflation adjustment. - title: H.R.1 - One Big Beautiful Bill Act diff --git a/uv.lock b/uv.lock index 6103cfbfe9d..17e0278145b 100644 --- a/uv.lock +++ b/uv.lock @@ -1244,7 +1244,7 @@ wheels = [ [[package]] name = "policyengine-us" -version = "1.398.1" +version = "1.401.0" source = { editable = "." } dependencies = [ { name = "microdf-python" }, From c3060262364fbd3b7da8d69f2276ee28fa01aaa5 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 16:56:18 -0400 Subject: [PATCH 14/53] Add propagate_metadata_to_children flag for parent-level uprating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add propagate_metadata_to_children: true to parameters that have uprating defined at the parent level (standard deduction, AMT exemption/phase-out, aged/blind additional). This ensures the uprating metadata propagates to each filing status child parameter, enabling automatic inflation adjustments to work correctly. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../gov/irs/deductions/standard/aged_or_blind/amount.yaml | 1 + .../parameters/gov/irs/deductions/standard/amount.yaml | 1 + .../parameters/gov/irs/income/amt/exemption/amount.yaml | 1 + .../parameters/gov/irs/income/amt/exemption/phase_out/start.yaml | 1 + 4 files changed, 4 insertions(+) diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml index 8e5cab629bd..b8cfcfa879d 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml @@ -60,6 +60,7 @@ metadata: - filing_status label: Additional standard deduction for the blind and aged period: year + propagate_metadata_to_children: true reference: - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=18 diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml index 0a0a644b39b..07a569de288 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml @@ -56,6 +56,7 @@ metadata: period: year breakdown: - filing_status + propagate_metadata_to_children: true reference: - title: H.R.1 - One Big Beautiful Bill Act href: https://www.congress.gov/bill/119th-congress/house-bill/1/text diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml index 0a6054cbe9d..3039dde178d 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml @@ -2,6 +2,7 @@ description: AMT exemption amount metadata: unit: currency-USD period: year + propagate_metadata_to_children: true uprating: parameter: gov.irs.uprating rounding: diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml index 06206b64579..6b27575e943 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml @@ -2,6 +2,7 @@ description: AMT exemption phase-out start metadata: unit: currency-USD period: year + propagate_metadata_to_children: true uprating: parameter: gov.irs.uprating rounding: From 9968cc36347b89411f36549a56e2aeab079a68b4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 17:04:13 -0400 Subject: [PATCH 15/53] Update C-CPI-U with latest BLS actuals through August 2025 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace outdated CPI data with actual BLS values: - 2024: All 12 months (Jan-Dec actual data) - 2025: Jan-Aug actual data (latest available from BLS as of Sep 2025) CBO forecasts retained for Sept 2025 onwards. Source: BLS Series SUUR0000SA0 via API (accessed Oct 2025) Previous file had only one 2024 value (Jan) and jumped straight to CBO forecasts. Now uses actual inflation data which affects all IRS parameter uprating calculations. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/bls/cpi/c_cpi_u.yaml | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/policyengine_us/parameters/gov/bls/cpi/c_cpi_u.yaml b/policyengine_us/parameters/gov/bls/cpi/c_cpi_u.yaml index e49224aefc4..0e0fbce96d0 100644 --- a/policyengine_us/parameters/gov/bls/cpi/c_cpi_u.yaml +++ b/policyengine_us/parameters/gov/bls/cpi/c_cpi_u.yaml @@ -289,12 +289,30 @@ values: 2023-10-01: 171.595 2023-11-01: 171.219 2023-12-01: 171.015 - 2024-01-01: 171.910 - # CBO projections. - # Since CBO projects the relevant tax parameter, - # which is the average over the 12mo ending August, - # we place that in February of each year. - 2025-02-01: 176.7 # 2026 value. + 2024-01-01: 171.649 + 2024-02-01: 172.700 + 2024-03-01: 173.796 + 2024-04-01: 174.424 + 2024-05-01: 174.685 + 2024-06-01: 174.721 + 2024-07-01: 174.792 + 2024-08-01: 174.848 + 2024-09-01: 175.099 + 2024-10-01: 175.282 + 2024-11-01: 175.129 + 2024-12-01: 175.144 + 2025-01-01: 176.268 + 2025-02-01: 177.032 + 2025-03-01: 177.415 + 2025-04-01: 177.959 + 2025-05-01: 178.318 + 2025-06-01: 178.909 + 2025-07-01: 179.154 + 2025-08-01: 179.656 + # CBO projections for September 2025 onwards. + # CBO projects the relevant tax parameter as the average over the 12mo ending August, + # which we place in February of each year for tax year calculations. + 2026-02-01: 176.7 # 2026 value from CBO Jan 2025 forecast. 2026-02-01: 180.5 # 2027 value. 2027-02-01: 184.1 # 2028 value. 2028-02-01: 187.8 # 2029 value. From e9d379d8e4507daf1fc4913deb05c93995b08888 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 17:04:44 -0400 Subject: [PATCH 16/53] Fix duplicate CPI entry and clarify comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove duplicate 2026-02-01 entry and clarify that CBO forecasts are for future tax year calculations (the 12-month average ending August determines the next year's tax parameters). ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- policyengine_us/parameters/gov/bls/cpi/c_cpi_u.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/policyengine_us/parameters/gov/bls/cpi/c_cpi_u.yaml b/policyengine_us/parameters/gov/bls/cpi/c_cpi_u.yaml index 0e0fbce96d0..4a2a473e7b1 100644 --- a/policyengine_us/parameters/gov/bls/cpi/c_cpi_u.yaml +++ b/policyengine_us/parameters/gov/bls/cpi/c_cpi_u.yaml @@ -309,10 +309,9 @@ values: 2025-06-01: 178.909 2025-07-01: 179.154 2025-08-01: 179.656 - # CBO projections for September 2025 onwards. - # CBO projects the relevant tax parameter as the average over the 12mo ending August, - # which we place in February of each year for tax year calculations. - 2026-02-01: 176.7 # 2026 value from CBO Jan 2025 forecast. + # CBO projections from CBO Jan 2025 forecast for future periods. + # CBO projects the average C-CPI-U over the 12mo ending August, + # which determines tax parameters for the following year. 2026-02-01: 180.5 # 2027 value. 2027-02-01: 184.1 # 2028 value. 2028-02-01: 187.8 # 2029 value. From 2a59a7ac2a6657fdd3a720140ffad72bce9273c1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 17:11:35 -0400 Subject: [PATCH 17/53] Remove duplicate 2026 CTC values and address review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove duplicate 2026 values from CTC parameters per review feedback: - CTC base amount: 2026 value unchanged from 2025 ($2,200) - CTC refundable max: 2026 value unchanged from 2024 ($1,700) Both will uprate starting in 2027 per OBBB inflation adjustment rules. Addresses review comments from @PavelMakarchuk. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml | 1 - .../gov/irs/credits/ctc/refundable/individual_max.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml index a8a5148cc09..3cdd9af1141 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml @@ -9,7 +9,6 @@ brackets: 2013-01-01: 1_000 2018-01-01: 2_000 2025-01-01: 2_200 - 2026-01-01: 2_200 2027-01-01: 2_300 2029-01-01: 2_400 2031-01-01: 2_500 diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml index 9f8beb2780f..c6e9861be43 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml @@ -6,7 +6,6 @@ values: 2022-01-01: 1_500 2023-01-01: 1_600 2024-01-01: 1_700 - 2026-01-01: 1_700 2027-01-01: 1_800 2029-01-01: 1_900 2032-01-01: 2_000 From d103eba663c0faa9b1c74946d270e5b1fbb672c4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 17:16:04 -0400 Subject: [PATCH 18/53] Remove unverified 2026 retirement contribution limit values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove 2026 values for IRA, 401(k), and catch-up contribution limits. These were based on news projections, not official IRS announcements. IRS has not yet officially released 2026 retirement contribution limits (typically announced in November). We should only include officially corroborated values. Will add these values once IRS officially announces them. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- changelog_entry.yaml | 4 +++- .../retirement_contributions/catch_up/limit/401k.yaml | 1 - .../retirement_contributions/catch_up/limit/ira.yaml | 1 - .../irs/gross_income/retirement_contributions/limit/401k.yaml | 1 - .../irs/gross_income/retirement_contributions/limit/ira.yaml | 1 - 5 files changed, 3 insertions(+), 5 deletions(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index 6f39532b913..fdc2ce5c202 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -1,6 +1,8 @@ - bump: patch changes: changed: - - Add 2026 IRS tax parameters from Revenue Procedure 2025-32, including standard deduction, tax brackets, EITC, AMT, retirement contribution limits, Child Tax Credit, capital gains brackets, QBI phase-out thresholds, and student loan interest deduction phase-out. + - Add 2026 IRS tax parameters from Revenue Procedure 2025-32, including standard deduction, tax brackets, EITC, AMT, Child Tax Credit, capital gains brackets, QBI phase-out thresholds, and student loan interest deduction phase-out. + - Update C-CPI-U with actual BLS data through August 2025. + - Add statutory rounding rules and uprating metadata for automatic inflation adjustments beyond 2026. fixed: - Correct previously incorrect 2026 QBI phase-out threshold forecast values. diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml index a869ae9a8f5..e46e00472ba 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml @@ -3,7 +3,6 @@ values: 2018-01-01: 6_000 2020-01-01: 6_500 2023-01-01: 7_500 - 2026-01-01: 8_000 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml index 0e22cc91152..4960dc38069 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml @@ -1,7 +1,6 @@ description: The US allows for catch-up IRA contributions of this amount. values: 2018-01-01: 1_000 - 2026-01-01: 1_100 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml index 8f27f1f7ca5..8374da9a970 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml @@ -7,7 +7,6 @@ values: 2023-01-01: 22_500 2024-01-01: 23_000 2025-01-01: 23_500 - 2026-01-01: 24_500 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml index 8eaa361a0d9..10c5b498a5e 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml @@ -4,7 +4,6 @@ values: 2019-01-01: 6_000 2023-01-01: 6_500 2024-01-01: 7_000 - 2026-01-01: 7_500 metadata: unit: currency-USD period: year From 043ff0fc42048f702ec55e11b177938626313b4a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 17:24:35 -0400 Subject: [PATCH 19/53] Add statutory uprating metadata to retirement contribution limits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add uprating metadata to IRA and 401(k) contribution limits per IRC ยง219(b)(5): - IRA base limit: round DOWN to $500 per IRC ยง219(b)(5)(C) - IRA catch-up: round DOWN to $100 per IRC ยง219(b)(5)(C)(ii) - 401(k) base limit: round DOWN to $500 per IRC ยง402(g)(4) - 401(k) catch-up: round DOWN to $500 per IRC ยง414(v)(2)(C) This enables automatic future adjustments when IRS announces 2026+ limits. The uprating will calculate projected values, but official IRS announcements (typically in November via IRS Notice) remain the authoritative source. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../retirement_contributions/catch_up/limit/401k.yaml | 5 +++++ .../retirement_contributions/catch_up/limit/ira.yaml | 5 +++++ .../gross_income/retirement_contributions/limit/401k.yaml | 5 +++++ .../irs/gross_income/retirement_contributions/limit/ira.yaml | 5 +++++ uv.lock | 2 +- 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml index e46e00472ba..818328099b4 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml @@ -7,6 +7,11 @@ metadata: unit: currency-USD period: year label: 401(k) catch-up amount + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 500 references: - title: 26 CFR ยง 1.414 - Catch-up contributions. (v)(2)(B)(i) href: https://www.law.cornell.edu/cfr/text/26/1.414(v)-1 diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml index 4960dc38069..2d81aae7717 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml @@ -5,6 +5,11 @@ metadata: unit: currency-USD period: year label: IRA catch-up amount + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 100 references: - title: 26 U.S. Code ยง 219 - Retirement savings (b)(5)(B) href: https://www.law.cornell.edu/uscode/text/26/219 diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml index 8374da9a970..ced5eddcded 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/401k.yaml @@ -11,6 +11,11 @@ metadata: unit: currency-USD period: year label: 401(k) contribution limit + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 500 references: - title: 26 CFR ยง 1.402(g)-1 - Limitation on exclusion for elective deferrals. (d))(i) href: https://law.cornell.edu/cfr/text/26/1.402(g)-1 diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml index 10c5b498a5e..04b5bb06907 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/limit/ira.yaml @@ -8,6 +8,11 @@ metadata: unit: currency-USD period: year label: IRA contribution limit + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 500 references: - title: 26 U.S. Code ยง 219 - Retirement savings (b)(5)(A) href: https://www.law.cornell.edu/uscode/text/26/219 diff --git a/uv.lock b/uv.lock index 33a27da27f5..ef1741b307a 100644 --- a/uv.lock +++ b/uv.lock @@ -1244,7 +1244,7 @@ wheels = [ [[package]] name = "policyengine-us" -version = "1.411.0" +version = "1.417.0" source = { editable = "." } dependencies = [ { name = "microdf-python" }, From a5f5be3cfb527437fbed66ba64e61da80f6ae0d6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 17:51:40 -0400 Subject: [PATCH 20/53] Replace legislative act references with IRC statutory references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace H.R.1 (OBBB) references with direct IRC section references now that OBBB has been enacted and the IRC updated (July 4, 2025). Legislative act references were temporary placeholders during the transition period. Now reference the authoritative IRC sections directly: - ยง1(j) for tax brackets - ยง24 for Child Tax Credit - ยง55(d) for AMT exemption and phase-out - ยง63(c) for standard deduction ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/irs/credits/ctc/amount/base.yaml | 10 ++-------- .../gov/irs/credits/ctc/refundable/individual_max.yaml | 5 ++--- .../parameters/gov/irs/deductions/standard/amount.yaml | 4 ++-- .../gov/irs/income/amt/exemption/amount.yaml | 5 ++--- .../gov/irs/income/amt/exemption/phase_out/start.yaml | 10 ++-------- policyengine_us/parameters/gov/irs/income/bracket.yaml | 5 ++--- 6 files changed, 12 insertions(+), 27 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml index 3cdd9af1141..0fd9a39e6ff 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml @@ -17,14 +17,8 @@ brackets: # Rose to $3,000/$3,600 in 2021. See arpa.yaml. metadata: reference: - - title: 26 U.S. Code ยง 24(a) - href: https://www.law.cornell.edu/uscode/text/26/24#a - - title: 26 U.S. Code ยง 24(h) - href: https://www.law.cornell.edu/uscode/text/26/24#h - - title: 26 U.S. Code ยง 24(i) - href: https://www.law.cornell.edu/uscode/text/26/24#i - - title: H.R.1 - One Big Beautiful Bill Act - href: https://www.congress.gov/bill/119th-congress/house-bill/1/text + - title: 26 U.S. Code ยง 24 - Child tax credit + href: https://www.law.cornell.edu/uscode/text/26/24 - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=13 # OBBB sets CTC value in $2,200 and uprates using 2024 as the base year. diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml index c6e9861be43..75ed8b5fe54 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml @@ -15,11 +15,10 @@ metadata: period: year label: Child tax credit refundable maximum amount reference: - - title: H.R.1 - One Big Beautiful Bill Act - href: https://www.congress.gov/bill/119th-congress/house-bill/1/text + - title: 26 U.S. Code ยง 24(h)(5) - Child tax credit refundable portion + href: https://www.law.cornell.edu/uscode/text/26/24#h_5 - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=13 - # OBBB extends TCJA CTC refundable maximum, sets base year to 2017 instead of 2016 for uprating. - title: 2025 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=8 - title: 2024 IRS data release diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml index 07a569de288..ecd6e4ef241 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/amount.yaml @@ -58,8 +58,8 @@ metadata: - filing_status propagate_metadata_to_children: true reference: - - title: H.R.1 - One Big Beautiful Bill Act - href: https://www.congress.gov/bill/119th-congress/house-bill/1/text + - title: 26 U.S. Code ยง 63(c) - Standard deduction + href: https://www.law.cornell.edu/uscode/text/26/63#c - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=18 - title: 2025 IRS data release diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml index 3039dde178d..baf94f484fb 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/amount.yaml @@ -9,9 +9,8 @@ metadata: type: nearest interval: 100 reference: - # OBBB extends TCJA AMT exemption amounts. - - title: H.R.1 - One Big Beautiful Bill Act - href: https://www.congress.gov/bill/119th-congress/house-bill/1/text + - title: 26 U.S. Code ยง 55(d) - Alternative minimum tax exemption + href: https://www.law.cornell.edu/uscode/text/26/55#d - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=16 - title: 2025 IRS data release diff --git a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml index 6b27575e943..2eb196caad8 100644 --- a/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/income/amt/exemption/phase_out/start.yaml @@ -9,14 +9,8 @@ metadata: type: nearest interval: 100 reference: - # OBBB extends TCJA AMT phase out start, but resets the inflation adjustment. - - title: H.R.1 - One Big Beautiful Bill Act - href: https://www.congress.gov/bill/119th-congress/house-bill/1/text - - title: 26 U.S. Code ยง 55 - Alternative minimum tax imposed (d)(2) - href: https://www.law.cornell.edu/uscode/text/26/55#d_2 - # TCJA amendment. - - title: 26 U.S. Code ยง 55 - Alternative minimum tax imposed (d)(4)(A)(ii) - href: https://www.law.cornell.edu/uscode/text/26/55#d_4_A_ii + - title: 26 U.S. Code ยง 55(d) - Alternative minimum tax exemption phase-out + href: https://www.law.cornell.edu/uscode/text/26/55#d - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=16 - title: 2025 IRS data release diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index 4c1b3cc3521..687fef27051 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -37,9 +37,8 @@ thresholds: period: year propagate_metadata_to_children: true reference: - - title: H.R.1 - One Big Beautiful Bill Act - href: https://www.congress.gov/bill/119th-congress/house-bill/1/text - # OBBB extended TCJA tax rates and brackets permanently. For 10% and 12% brackets, changed base year from 2017 to 2016 for inflation adjustments. + - title: 26 U.S. Code ยง 1(j) - Tax rate tables + href: https://www.law.cornell.edu/uscode/text/26/1#j - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=10 - title: 2025 IRS data release From 1469aad67aee8e0baff311273663a7020eb9b36b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 17:52:09 -0400 Subject: [PATCH 21/53] Remove OBBB historical comment from CTC base parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove comment about OBBB setting CTC values - this is now captured in the IRC ยง24 which is the authoritative reference. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml index 0fd9a39e6ff..5cfa600cd26 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml @@ -21,7 +21,6 @@ brackets: href: https://www.law.cornell.edu/uscode/text/26/24 - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=13 - # OBBB sets CTC value in $2,200 and uprates using 2024 as the base year. - threshold: values: 2013-01-01: 17 From 65375011ec2d6f19796eb2bfe6a791b8db9317c7 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 17:56:16 -0400 Subject: [PATCH 22/53] Update CPI-U and CPI-W with latest BLS actuals; add capital gains uprating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update inflation indices with actual BLS data through August 2025: - CPI-U: All 2024 months and Jan-Aug 2025 actuals - CPI-W: All 2024 months and Jan-Aug 2025 actuals - C-CPI-U: Already updated in previous commit Add uprating metadata to capital gains brackets per IRC ยง1(j)(5)(C): - Round DOWN to nearest $50 - Uses gov.irs.uprating (same as other IRS parameters) Replace legislative references (OBBB) with IRC statutory sections since OBBB is now enacted law (July 4, 2025). Change bump to minor (adding new year parameters is minor, not patch). ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- changelog_entry.yaml | 2 +- .../parameters/gov/bls/cpi/cpi_u.yaml | 39 ++++++++++--------- .../gov/irs/capital_gains/brackets.yaml | 6 +++ 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index fdc2ce5c202..dcd0ca63727 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -1,4 +1,4 @@ -- bump: patch +- bump: minor changes: changed: - Add 2026 IRS tax parameters from Revenue Procedure 2025-32, including standard deduction, tax brackets, EITC, AMT, Child Tax Credit, capital gains brackets, QBI phase-out thresholds, and student loan interest deduction phase-out. diff --git a/policyengine_us/parameters/gov/bls/cpi/cpi_u.yaml b/policyengine_us/parameters/gov/bls/cpi/cpi_u.yaml index 28081e43178..a7304f4cd9d 100644 --- a/policyengine_us/parameters/gov/bls/cpi/cpi_u.yaml +++ b/policyengine_us/parameters/gov/bls/cpi/cpi_u.yaml @@ -1332,24 +1332,27 @@ values: 2023-10-01: 307.671 2023-11-01: 307.051 2023-12-01: 306.746 - 2024-01-01: 308.417 - 2024-02-01: 310.326 - 2024-03-01: 312.332 - 2024-04-01: 313.548 - 2024-05-01: 314.069 - 2024-06-01: 314.175 - 2024-07-01: 314.54 - 2024-08-01: 314.796 - 2024-09-01: 315.301 - 2024-10-01: 315.664 - 2024-11-01: 315.493 - 2024-12-01: 315.605 - - # CBO projections. - # Since CBO projects the relevant tax parameter, - # which is the average over the 12mo ending August, - # we place that in February of each year. - 2025-02-01: 318.2 # 2026 value. + 2024-01-01: 309.794 + 2024-02-01: 311.022 + 2024-03-01: 312.107 + 2024-04-01: 313.016 + 2024-05-01: 313.140 + 2024-06-01: 313.131 + 2024-07-01: 313.566 + 2024-08-01: 314.131 + 2024-09-01: 314.851 + 2024-10-01: 315.564 + 2024-11-01: 316.449 + 2024-12-01: 317.603 + 2025-01-01: 319.086 + 2025-02-01: 319.775 + 2025-03-01: 319.615 + 2025-04-01: 320.321 + 2025-05-01: 320.580 + 2025-06-01: 321.500 + 2025-07-01: 322.132 + 2025-08-01: 323.364 + # CBO projections from CBO Jan 2025 forecast for future periods. 2026-02-01: 325.9 # 2027 value. 2027-02-01: 333.5 # 2028 value. 2028-02-01: 341.1 # 2029 value. diff --git a/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml b/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml index 116cb9f5f5c..c155ec29bc8 100644 --- a/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml +++ b/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml @@ -15,6 +15,12 @@ thresholds: metadata: unit: currency-USD period: year + propagate_metadata_to_children: true + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 50 reference: - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=12 From 47a71d999f87dca524eebf6a9ea93a3d80f19c55 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 17:59:39 -0400 Subject: [PATCH 23/53] Update CPI-W with actual BLS monthly data through August 2025 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace quarterly-interpolated values with actual monthly BLS data: - 2024: All 12 months corrected with BLS actuals - 2025: Jan-Aug actual monthly data added Retain explanatory comment about CBO forecast methodology (12-month average ending August placed in February for tax calculations). Source: BLS Series CWSR0000SA0 via API (accessed Oct 2025) ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/bls/cpi/cpi_w.yaml | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/policyengine_us/parameters/gov/bls/cpi/cpi_w.yaml b/policyengine_us/parameters/gov/bls/cpi/cpi_w.yaml index 2f60b02219e..1ca632fb421 100644 --- a/policyengine_us/parameters/gov/bls/cpi/cpi_w.yaml +++ b/policyengine_us/parameters/gov/bls/cpi/cpi_w.yaml @@ -1332,28 +1332,31 @@ values: 2023-10-01: 302.071 2023-11-01: 301.224 2023-12-01: 300.728 - 2024-01-01: 302.201 - 2024-02-01: 304.284 - 2024-03-01: 306.502 - 2024-04-01: 307.811 - 2024-05-01: 308.163 - 2024-06-01: 308.054 - 2024-07-01: 308.501 - 2024-08-01: 308.640 - 2024-09-01: 309.046 - 2024-10-01: 309.358 - 2024-11-01: 308.998 - 2024-12-01: 309.067 - # CBO projections based on quarterly % change to CPI-U. - # Anchored to CPI-U 2024Q1 average of 310.989. - # In 2024Q1, CPI-W averaged to (304.284 + 306.502 + 307.811) / 3 = 306.199. - # So assign second month of each quarter with the CBO quarterly value * 306.199 / 310.989 = 0.985. - # Uprating parameters don't support arithmetic so pass values. - 2025-02-01: 311.407 # 2025Q1: 317.9 * 309.141 / 315.587 - 2025-05-01: 312.974 # 2025Q2: 319.5 * 309.141 / 315.587 - 2025-08-01: 314.933 # 2025Q3: 321.5 * 309.141 / 315.587 - 2025-11-01: 316.990 # 2025Q4: 323.6 * 309.141 / 315.587 - 2026-02-01: 318.949 # 2026Q1: 325.6 * 309.141 / 315.587 + 2024-01-01: 303.733 + 2024-02-01: 305.121 + 2024-03-01: 306.349 + 2024-04-01: 307.211 + 2024-05-01: 307.233 + 2024-06-01: 307.010 + 2024-07-01: 307.410 + 2024-08-01: 307.843 + 2024-09-01: 308.539 + 2024-10-01: 309.161 + 2024-11-01: 309.945 + 2024-12-01: 311.162 + 2025-01-01: 312.776 + 2025-02-01: 313.323 + 2025-03-01: 313.175 + 2025-04-01: 313.729 + 2025-05-01: 313.961 + 2025-06-01: 314.858 + 2025-07-01: 315.279 + 2025-08-01: 316.530 + # CBO projections from CBO Jan 2025 forecast for future periods. + # Since CBO projects the relevant tax parameter, + # which is the average over the 12mo ending August, + # we place that in February of each year. + 2026-02-01: 318.949 # 2026Q1 from CBO Jan 2025 forecast. 2026-05-01: 320.811 # 2026Q2: 327.5 * 309.141 / 315.587 2026-08-01: 322.672 # 2026Q3: 329.4 * 309.141 / 315.587 2026-11-01: 324.533 # 2026Q4: 331.3 * 309.141 / 315.587 From ff864d4b19b031326259a8375d56573f82650c4c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:00:43 -0400 Subject: [PATCH 24/53] Split capital gains brackets into separate rates and thresholds files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split capital_gains/brackets.yaml into two files following convention: - rates.yaml: Capital gains tax rates (0%, 15%, 20%) - thresholds.yaml: Income thresholds by filing status with uprating This follows the organizational pattern used elsewhere in the codebase and makes the structure clearer. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../gov/irs/capital_gains/brackets.yaml | 206 ------------------ .../gov/irs/capital_gains/rates.yaml | 10 + .../gov/irs/capital_gains/thresholds.yaml | 191 ++++++++++++++++ 3 files changed, 201 insertions(+), 206 deletions(-) delete mode 100644 policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml create mode 100644 policyengine_us/parameters/gov/irs/capital_gains/rates.yaml create mode 100644 policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml diff --git a/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml b/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml deleted file mode 100644 index c155ec29bc8..00000000000 --- a/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml +++ /dev/null @@ -1,206 +0,0 @@ -description: Capital gains tax thresholds and rates -rates: - description: Long term capital gain and qualified dividends (regular/non-AMT) rate - 1: - 2013-01-01: 0 - 2: - 2013-01-01: 0.15 - 3: - 2013-01-01: 0.2 - - metadata: - unit: /1 -thresholds: - description: Top thresholds of long-term capital gains and qualified dividends (regular/non-AMT) tax brackets - metadata: - unit: currency-USD - period: year - propagate_metadata_to_children: true - uprating: - parameter: gov.irs.uprating - rounding: - type: downwards - interval: 50 - reference: - - title: 2026 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=12 - - title: 2025 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=7 - - title: 2024 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-23-34.pdf#page=8 - - title: 2023 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-22-38.pdf#page=8 - - title: 2022 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-21-45.pdf#page=8 - - title: 2021 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-20-45.pdf#page=8 - - title: 2020 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-19-44.pdf#page=8 - - title: 2019 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=8 - - title: Internal Revenue Code ยง1(j)(5)(B) - # IRC defines amounts as of 2018. - # Defines COLA with rounded up to the nearest multiple of $50. - href: https://www.law.cornell.edu/uscode/text/26/1#j_5_B - 1: - HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 48_600 - 2014-01-01: 49_400 - 2015-01-01: 50_200 - 2016-01-01: 50_400 - 2017-01-01: 50_800 - 2018-01-01: 51_700 - 2019-01-01: 52_750 - 2020-01-01: 53_600 - 2021-01-01: 54_100 - 2022-01-01: 55_800 - 2023-01-01: 59_750 - 2024-01-01: 63_000 - 2025-01-01: 64_750 - 2026-01-01: 66_200 - JOINT: - values: - 2013-01-01: 72_500 - 2014-01-01: 73_800 - 2015-01-01: 74_900 - 2016-01-01: 75_300 - 2017-01-01: 75_900 - 2018-01-01: 77_200 - 2019-01-01: 78_750 - 2020-01-01: 80_000 - 2021-01-01: 80_800 - 2022-01-01: 83_350 - 2023-01-01: 89_250 - 2024-01-01: 94_050 - 2025-01-01: 96_700 - 2026-01-01: 98_900 - SEPARATE: - values: - 2013-01-01: 36_250 - 2014-01-01: 36_900 - 2015-01-01: 37_450 - 2016-01-01: 37_650 - 2017-01-01: 37_950 - 2018-01-01: 38_600 - 2019-01-01: 39_375 - 2020-01-01: 40_000 - 2021-01-01: 40_400 - 2022-01-01: 41_675 - 2023-01-01: 44_625 - 2024-01-01: 47_025 - 2025-01-01: 48_350 - 2026-01-01: 49_450 - SINGLE: - values: - 2013-01-01: 36_250 - 2014-01-01: 36_900 - 2015-01-01: 37_450 - 2016-01-01: 37_650 - 2017-01-01: 37_950 - 2018-01-01: 38_600 - 2019-01-01: 39_375 - 2020-01-01: 40_000 - 2021-01-01: 40_400 - 2022-01-01: 41_675 - 2023-01-01: 44_625 - 2024-01-01: 47_025 - 2025-01-01: 48_350 - 2026-01-01: 49_450 - SURVIVING_SPOUSE: - values: - 2013-01-01: 72_500 - 2014-01-01: 73_800 - 2015-01-01: 74_900 - 2016-01-01: 75_300 - 2017-01-01: 75_900 - 2018-01-01: 77_200 - 2019-01-01: 78_750 - 2020-01-01: 80_000 - 2021-01-01: 80_800 - 2022-01-01: 83_350 - 2023-01-01: 89_250 - 2024-01-01: 94_050 - 2025-01-01: 96_700 - 2026-01-01: 98_900 - 2: - HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 425_000 - 2014-01-01: 432_200 - 2015-01-01: 439_000 - 2016-01-01: 441_000 - 2017-01-01: 444_550 - 2018-01-01: 452_400 - 2019-01-01: 461_700 - 2020-01-01: 469_050 - 2021-01-01: 473_750 - 2022-01-01: 488_500 - 2023-01-01: 523_050 - 2024-01-01: 551_350 - 2025-01-01: 566_700 - 2026-01-01: 579_600 - JOINT: - values: - 2013-01-01: 450_000 - 2014-01-01: 457_600 - 2015-01-01: 464_850 - 2016-01-01: 466_950 - 2017-01-01: 470_700 - 2018-01-01: 479_000 - 2019-01-01: 488_850 - 2020-01-01: 496_600 - 2021-01-01: 501_600 - 2022-01-01: 517_200 - 2023-01-01: 553_850 - 2024-01-01: 583_750 - 2025-01-01: 600_050 - 2026-01-01: 613_700 - SEPARATE: - values: - 2013-01-01: 225_000 - 2014-01-01: 228_800 - 2015-01-01: 232_425 - 2016-01-01: 233_475 - 2017-01-01: 235_350 - 2018-01-01: 239_500 - 2019-01-01: 244_425 - 2020-01-01: 248_300 - 2021-01-01: 250_800 - 2022-01-01: 258_600 - 2023-01-01: 276_900 - 2024-01-01: 291_850 - 2025-01-01: 300_000 - 2026-01-01: 306_850 - SINGLE: - values: - 2013-01-01: 400_000 - 2014-01-01: 406_750 - 2015-01-01: 413_200 - 2016-01-01: 415_050 - 2017-01-01: 418_400 - 2018-01-01: 425_800 - 2019-01-01: 434_550 - 2020-01-01: 441_450 - 2021-01-01: 445_850 - 2022-01-01: 459_750 - 2023-01-01: 492_300 - 2024-01-01: 518_900 - 2025-01-01: 533_400 - 2026-01-01: 545_500 - SURVIVING_SPOUSE: - values: - 2013-01-01: 450_000 - 2014-01-01: 457_600 - 2015-01-01: 464_850 - 2016-01-01: 466_950 - 2017-01-01: 470_700 - 2018-01-01: 479_000 - 2019-01-01: 488_850 - 2020-01-01: 496_600 - 2021-01-01: 501_600 - 2022-01-01: 517_200 - 2023-01-01: 553_850 - 2024-01-01: 583_750 - 2025-01-01: 600_050 - 2026-01-01: 613_700 diff --git a/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml b/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml new file mode 100644 index 00000000000..09590fc62c1 --- /dev/null +++ b/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml @@ -0,0 +1,10 @@ +description: Long term capital gain and qualified dividends (regular/non-AMT) rate +1: + 2013-01-01: 0 +2: + 2013-01-01: 0.15 +3: + 2013-01-01: 0.2 + +metadata: + unit: /1 diff --git a/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml new file mode 100644 index 00000000000..96285309a15 --- /dev/null +++ b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml @@ -0,0 +1,191 @@ +description: Top thresholds of long-term capital gains and qualified dividends (regular/non-AMT) tax brackets +metadata: + unit: currency-USD + period: year + propagate_metadata_to_children: true + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 50 + reference: + - title: 26 U.S. Code ยง 1(j)(5)(B) - Capital gains rate thresholds + href: https://www.law.cornell.edu/uscode/text/26/1#j_5_B + - title: 2026 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=12 + - title: 2025 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-24-40.pdf#page=7 + - title: 2024 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-23-34.pdf#page=8 + - title: 2023 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-22-38.pdf#page=8 + - title: 2022 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-21-45.pdf#page=8 + - title: 2021 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-20-45.pdf#page=8 + - title: 2020 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-19-44.pdf#page=8 + - title: 2019 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=8 +1: + HEAD_OF_HOUSEHOLD: + values: + 2013-01-01: 48_600 + 2014-01-01: 49_400 + 2015-01-01: 50_200 + 2016-01-01: 50_400 + 2017-01-01: 50_800 + 2018-01-01: 51_700 + 2019-01-01: 52_750 + 2020-01-01: 53_600 + 2021-01-01: 54_100 + 2022-01-01: 55_800 + 2023-01-01: 59_750 + 2024-01-01: 63_000 + 2025-01-01: 64_750 + 2026-01-01: 66_200 + JOINT: + values: + 2013-01-01: 72_500 + 2014-01-01: 73_800 + 2015-01-01: 74_900 + 2016-01-01: 75_300 + 2017-01-01: 75_900 + 2018-01-01: 77_200 + 2019-01-01: 78_750 + 2020-01-01: 80_000 + 2021-01-01: 80_800 + 2022-01-01: 83_350 + 2023-01-01: 89_250 + 2024-01-01: 94_050 + 2025-01-01: 96_700 + 2026-01-01: 98_900 + SEPARATE: + values: + 2013-01-01: 36_250 + 2014-01-01: 36_900 + 2015-01-01: 37_450 + 2016-01-01: 37_650 + 2017-01-01: 37_950 + 2018-01-01: 38_600 + 2019-01-01: 39_375 + 2020-01-01: 40_000 + 2021-01-01: 40_400 + 2022-01-01: 41_675 + 2023-01-01: 44_625 + 2024-01-01: 47_025 + 2025-01-01: 48_350 + 2026-01-01: 49_450 + SINGLE: + values: + 2013-01-01: 36_250 + 2014-01-01: 36_900 + 2015-01-01: 37_450 + 2016-01-01: 37_650 + 2017-01-01: 37_950 + 2018-01-01: 38_600 + 2019-01-01: 39_375 + 2020-01-01: 40_000 + 2021-01-01: 40_400 + 2022-01-01: 41_675 + 2023-01-01: 44_625 + 2024-01-01: 47_025 + 2025-01-01: 48_350 + 2026-01-01: 49_450 + SURVIVING_SPOUSE: + values: + 2013-01-01: 72_500 + 2014-01-01: 73_800 + 2015-01-01: 74_900 + 2016-01-01: 75_300 + 2017-01-01: 75_900 + 2018-01-01: 77_200 + 2019-01-01: 78_750 + 2020-01-01: 80_000 + 2021-01-01: 80_800 + 2022-01-01: 83_350 + 2023-01-01: 89_250 + 2024-01-01: 94_050 + 2025-01-01: 96_700 + 2026-01-01: 98_900 +2: + HEAD_OF_HOUSEHOLD: + values: + 2013-01-01: 425_000 + 2014-01-01: 432_200 + 2015-01-01: 439_000 + 2016-01-01: 441_000 + 2017-01-01: 444_550 + 2018-01-01: 452_400 + 2019-01-01: 461_700 + 2020-01-01: 469_050 + 2021-01-01: 473_750 + 2022-01-01: 488_500 + 2023-01-01: 523_050 + 2024-01-01: 551_350 + 2025-01-01: 566_700 + 2026-01-01: 579_600 + JOINT: + values: + 2013-01-01: 450_000 + 2014-01-01: 457_600 + 2015-01-01: 464_850 + 2016-01-01: 466_950 + 2017-01-01: 470_700 + 2018-01-01: 479_000 + 2019-01-01: 488_850 + 2020-01-01: 496_600 + 2021-01-01: 501_600 + 2022-01-01: 517_200 + 2023-01-01: 553_850 + 2024-01-01: 583_750 + 2025-01-01: 600_050 + 2026-01-01: 613_700 + SEPARATE: + values: + 2013-01-01: 225_000 + 2014-01-01: 228_800 + 2015-01-01: 232_425 + 2016-01-01: 233_475 + 2017-01-01: 235_350 + 2018-01-01: 239_500 + 2019-01-01: 244_425 + 2020-01-01: 248_300 + 2021-01-01: 250_800 + 2022-01-01: 258_600 + 2023-01-01: 276_900 + 2024-01-01: 291_850 + 2025-01-01: 300_000 + 2026-01-01: 306_850 + SINGLE: + values: + 2013-01-01: 400_000 + 2014-01-01: 406_750 + 2015-01-01: 413_200 + 2016-01-01: 415_050 + 2017-01-01: 418_400 + 2018-01-01: 425_800 + 2019-01-01: 434_550 + 2020-01-01: 441_450 + 2021-01-01: 445_850 + 2022-01-01: 459_750 + 2023-01-01: 492_300 + 2024-01-01: 518_900 + 2025-01-01: 533_400 + 2026-01-01: 545_500 + SURVIVING_SPOUSE: + values: + 2013-01-01: 450_000 + 2014-01-01: 457_600 + 2015-01-01: 464_850 + 2016-01-01: 466_950 + 2017-01-01: 470_700 + 2018-01-01: 479_000 + 2019-01-01: 488_850 + 2020-01-01: 496_600 + 2021-01-01: 501_600 + 2022-01-01: 517_200 + 2023-01-01: 553_850 + 2024-01-01: 583_750 + 2025-01-01: 600_050 + 2026-01-01: 613_700 From baf5e10b1c98ea244e4dd78325be314fd223f847 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:03:38 -0400 Subject: [PATCH 25/53] Add missing uprating metadata and rounding rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add statutory uprating metadata to parameters that were missing it: 1. QBI phase-out start: Add rounding DOWN to $25 per IRC ยง1(f)(7) (same as tax bracket thresholds for single/separate filers) 2. CTC base amount: Add uprating with rounding DOWN to $100 per IRC ยง24(i)(3) 3. CTC refundable max: Add uprating with rounding DOWN to $100 per IRC ยง24(i)(3) All three parameters inflate annually per their respective IRC sections and now have complete uprating metadata for automatic adjustments. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/irs/credits/ctc/amount/base.yaml | 6 ++++++ .../gov/irs/credits/ctc/refundable/individual_max.yaml | 5 +++++ .../parameters/gov/irs/deductions/qbi/phase_out/start.yaml | 6 +++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml index 5cfa600cd26..0eef2207c4b 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml @@ -39,3 +39,9 @@ metadata: amount_unit: currency-USD amount_period: year label: Child Tax Credit amount + propagate_metadata_to_children: true + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 100 diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml index 75ed8b5fe54..d7033c83c76 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml @@ -14,6 +14,11 @@ metadata: unit: currency-USD period: year label: Child tax credit refundable maximum amount + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 100 reference: - title: 26 U.S. Code ยง 24(h)(5) - Child tax credit refundable portion href: https://www.law.cornell.edu/uscode/text/26/24#h_5 diff --git a/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml index 1bb98c72adb..300c7e18912 100644 --- a/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml @@ -20,8 +20,12 @@ metadata: href: https://www.irs.gov/pub/irs-drop/rp-20-45.pdf#page=16 - title: 2020 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-19-44.pdf#page=16 - uprating: gov.irs.uprating propagate_metadata_to_children: true + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 25 HEAD_OF_HOUSEHOLD: 2013-01-01: 0 From 4e10e3b420e82d4b79a2028f51a2d49cc348c4cb Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:09:45 -0400 Subject: [PATCH 26/53] Add labels and references to capital gains parameter files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add proper metadata to capital gains files: - rates.yaml: Add label and IRC references (ยง1(h) and ยง1(j)(5)(A)) - thresholds.yaml: Add label - Rephrase descriptions in active voice per PolicyEngine standards ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/irs/capital_gains/rates.yaml | 8 +++++++- .../parameters/gov/irs/capital_gains/thresholds.yaml | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml b/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml index 09590fc62c1..5a61a9a391c 100644 --- a/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml +++ b/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml @@ -1,4 +1,4 @@ -description: Long term capital gain and qualified dividends (regular/non-AMT) rate +description: The IRS taxes long-term capital gains and qualified dividends at these rates. 1: 2013-01-01: 0 2: @@ -8,3 +8,9 @@ description: Long term capital gain and qualified dividends (regular/non-AMT) ra metadata: unit: /1 + label: Capital gains tax rates + reference: + - title: 26 U.S. Code ยง 1(h) - Tax on net capital gain + href: https://www.law.cornell.edu/uscode/text/26/1#h + - title: 26 U.S. Code ยง 1(j)(5)(A) - Capital gains rates + href: https://www.law.cornell.edu/uscode/text/26/1#j_5_A diff --git a/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml index 96285309a15..2a757287caa 100644 --- a/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml +++ b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml @@ -1,7 +1,8 @@ -description: Top thresholds of long-term capital gains and qualified dividends (regular/non-AMT) tax brackets +description: The IRS applies higher capital gains rates above these income thresholds. metadata: unit: currency-USD period: year + label: Capital gains tax rate thresholds propagate_metadata_to_children: true uprating: parameter: gov.irs.uprating From 61804d60653265e11e4a083d6e4d078d121dc220 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:11:29 -0400 Subject: [PATCH 27/53] Clarify changelog to distinguish new vs changed uprating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate changelog into: - Added: New uprating for parameters with no post-2025 values - Changed: Switched from manual CBO forecasts to automatic uprating - Fixed: Corrected incorrect forecast values This makes it clear which parameters gained uprating capability vs which were simplified from manual to automatic. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- changelog_entry.yaml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index dcd0ca63727..3d6e8ab2a0a 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -1,8 +1,12 @@ - bump: minor changes: + added: + - 2026 IRS tax parameters from Revenue Procedure 2025-32 (standard deduction, tax brackets, EITC, AMT, CTC, capital gains, QBI phase-out, student loan interest deduction, aged/blind additional). + - Automatic uprating with statutory rounding rules for parameters that previously had no post-2025 values (tax brackets 1-2, retirement contributions, capital gains thresholds, CTC base and refundable amounts, QBI phase-out). + - Actual BLS CPI data through August 2025 (C-CPI-U, CPI-U, CPI-W). changed: - - Add 2026 IRS tax parameters from Revenue Procedure 2025-32, including standard deduction, tax brackets, EITC, AMT, Child Tax Credit, capital gains brackets, QBI phase-out thresholds, and student loan interest deduction phase-out. - - Update C-CPI-U with actual BLS data through August 2025. - - Add statutory rounding rules and uprating metadata for automatic inflation adjustments beyond 2026. + - Replaced manual CBO forecast values (2027-2035) with automatic uprating for tax brackets 3-6, standard deduction, AMT exemption, AMT phase-out, and aged/blind additional deduction. + - Replaced OBBB legislative references with permanent IRC statutory sections. + - Split capital_gains/brackets.yaml into separate rates.yaml and thresholds.yaml files. fixed: - - Correct previously incorrect 2026 QBI phase-out threshold forecast values. + - Corrected previously incorrect 2026 QBI phase-out threshold forecast values. From dbda2edd404b5c3d53a2a2e47a042b56bc68e80d Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:17:03 -0400 Subject: [PATCH 28/53] Fix CTC uprating to avoid inflating age thresholds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move uprating metadata from parent level to the amount bracket only. The CTC uses a bracket structure where thresholds are AGES (17 years), not dollar amounts - these should never be uprated. Also correct changelog: CTC already had manual forecast values (2027-2035), so this is "changed from manual to automatic", not "added". ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- changelog_entry.yaml | 4 ++-- .../parameters/gov/irs/credits/ctc/amount/base.yaml | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index 3d6e8ab2a0a..ab270672119 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -2,10 +2,10 @@ changes: added: - 2026 IRS tax parameters from Revenue Procedure 2025-32 (standard deduction, tax brackets, EITC, AMT, CTC, capital gains, QBI phase-out, student loan interest deduction, aged/blind additional). - - Automatic uprating with statutory rounding rules for parameters that previously had no post-2025 values (tax brackets 1-2, retirement contributions, capital gains thresholds, CTC base and refundable amounts, QBI phase-out). + - Automatic uprating with statutory rounding rules for parameters that previously had no post-2025 values (tax brackets 1-2, retirement contributions, capital gains thresholds, QBI phase-out). - Actual BLS CPI data through August 2025 (C-CPI-U, CPI-U, CPI-W). changed: - - Replaced manual CBO forecast values (2027-2035) with automatic uprating for tax brackets 3-6, standard deduction, AMT exemption, AMT phase-out, and aged/blind additional deduction. + - Replaced manual CBO forecast values (2027-2035) with automatic uprating for tax brackets 3-6, standard deduction, AMT exemption, AMT phase-out, aged/blind additional deduction, CTC base amount, and CTC refundable maximum. - Replaced OBBB legislative references with permanent IRC statutory sections. - Split capital_gains/brackets.yaml into separate rates.yaml and thresholds.yaml files. fixed: diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml index 0eef2207c4b..32ae4a7250f 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml @@ -16,6 +16,11 @@ brackets: 2035-01-01: 2_700 # Rose to $3,000/$3,600 in 2021. See arpa.yaml. metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: downwards + interval: 100 reference: - title: 26 U.S. Code ยง 24 - Child tax credit href: https://www.law.cornell.edu/uscode/text/26/24 @@ -39,9 +44,3 @@ metadata: amount_unit: currency-USD amount_period: year label: Child Tax Credit amount - propagate_metadata_to_children: true - uprating: - parameter: gov.irs.uprating - rounding: - type: downwards - interval: 100 From e8678ddaf9129c59e952cf41d596113c16f197b6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:20:15 -0400 Subject: [PATCH 29/53] Fix CTC refundable reference and correct base amount comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add IRC ยง24(i)(1) reference (inflation adjustment section) - Correct comment: base is $1,400 (TCJA 2018), not $1,000 - Remove duplicate reference (ยง24(h)(5) vs ยง24(h)(5)(A) - keep the more specific one) The statute adjusts the $1,400 amount using calendar year 2017 as the base year for inflation calculations, not $1,000. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../irs/credits/ctc/refundable/individual_max.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml index d7033c83c76..f297ab76612 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml @@ -20,8 +20,10 @@ metadata: type: downwards interval: 100 reference: - - title: 26 U.S. Code ยง 24(h)(5) - Child tax credit refundable portion - href: https://www.law.cornell.edu/uscode/text/26/24#h_5 + - title: 26 U.S. Code ยง 24(h)(5)(A) - Child tax credit refundable portion + href: https://www.law.cornell.edu/uscode/text/26/24#h_5_A + - title: 26 U.S. Code ยง 24(i)(1) - Inflation adjustment for refundable amount + href: https://www.law.cornell.edu/uscode/text/26/24#i_1 - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=13 - title: 2025 IRS data release @@ -38,7 +40,4 @@ metadata: href: https://www.irs.gov/pub/irs-drop/rp-19-44.pdf#page=10 - title: 2019 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=12 - # NB: Not specified in prior IRS data releases. - # Indexed to inflation with respect to 2017, rounding down by $100. - - title: 26 U.S. Code ยง 24(h)(5)(A) - href: https://www.law.cornell.edu/uscode/text/26/24#h_5_A + # TCJA set base at $1,400 in 2018. IRC ยง24(i)(1) adjusts from 2017 base year. From 8195fe3ab8cc49a2e78f71baac0c2e3ebcd0cf05 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:25:16 -0400 Subject: [PATCH 30/53] Correct changelog: QBI phase-out already had uprating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QBI phase-out start already had uprating metadata, we only added the missing rounding rules ($25 down per IRC ยง1(f)(7)). Move from "added uprating" to "added rounding rules" category. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- changelog_entry.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog_entry.yaml b/changelog_entry.yaml index ab270672119..b1b594405d0 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -2,7 +2,8 @@ changes: added: - 2026 IRS tax parameters from Revenue Procedure 2025-32 (standard deduction, tax brackets, EITC, AMT, CTC, capital gains, QBI phase-out, student loan interest deduction, aged/blind additional). - - Automatic uprating with statutory rounding rules for parameters that previously had no post-2025 values (tax brackets 1-2, retirement contributions, capital gains thresholds, QBI phase-out). + - Automatic uprating with statutory rounding rules for parameters that previously had no post-2025 values (tax brackets 1-2, retirement contributions, capital gains thresholds). + - Statutory rounding rules to parameters with uprating but no rounding (QBI phase-out). - Actual BLS CPI data through August 2025 (C-CPI-U, CPI-U, CPI-W). changed: - Replaced manual CBO forecast values (2027-2035) with automatic uprating for tax brackets 3-6, standard deduction, AMT exemption, AMT phase-out, aged/blind additional deduction, CTC base amount, and CTC refundable maximum. From 192d8510dacb94e676e944d49f2b189032281cd1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:33:26 -0400 Subject: [PATCH 31/53] Clarify CTC refundable uprating implementation note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comment explaining that IRC ยง24(i)(1) technically requires calculating from the $1,400 statutory base using 2017 as reference year, but our current incremental uprating (from latest value) produces correct results. This documents the implementation approach for future reference, in case base-year uprating support is added to policyengine-core. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../gov/irs/credits/ctc/refundable/individual_max.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml index f297ab76612..faef1bcfc99 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/refundable/individual_max.yaml @@ -40,4 +40,6 @@ metadata: href: https://www.irs.gov/pub/irs-drop/rp-19-44.pdf#page=10 - title: 2019 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=12 - # TCJA set base at $1,400 in 2018. IRC ยง24(i)(1) adjusts from 2017 base year. + # IRC ยง24(i)(1) adjusts the $1,400 statutory base using 2017 as reference year. + # Current implementation uses incremental uprating from latest value, which + # produces correct results but differs from statutory base-year calculation. From 955c0fe2f683cc650d59e539330719ea68efd409 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:34:43 -0400 Subject: [PATCH 32/53] Document CTC amount uprating limitation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comment to CTC amount explaining that IRC ยง24(i)(2) requires calculating from $2,200 base using 2024 as reference year (fixed-base), but current implementation uses incremental uprating. Add IRC ยง24(i)(2) reference for inflation adjustment mechanism. Incremental approach produces correct near-term values but may drift from statutory calculation over long periods. Documents limitation for future improvement if policyengine-core adds base-year uprating. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/irs/credits/ctc/amount/base.yaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml index 32ae4a7250f..2a5f73e2b72 100644 --- a/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml +++ b/policyengine_us/parameters/gov/irs/credits/ctc/amount/base.yaml @@ -22,10 +22,15 @@ brackets: type: downwards interval: 100 reference: - - title: 26 U.S. Code ยง 24 - Child tax credit - href: https://www.law.cornell.edu/uscode/text/26/24 + - title: 26 U.S. Code ยง 24(h)(2) - Child tax credit amount + href: https://www.law.cornell.edu/uscode/text/26/24#h_2 + - title: 26 U.S. Code ยง 24(i)(2) - Inflation adjustment for credit amount + href: https://www.law.cornell.edu/uscode/text/26/24#i_2 - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=13 + # IRC ยง24(i)(2) adjusts the $2,200 statutory base using 2024 as reference year. + # Current implementation uses incremental uprating from latest value, which + # produces correct results but differs from statutory base-year calculation. - threshold: values: 2013-01-01: 17 From 17b45cb01177cb9c2fcc1f38e122c039ad55f876 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:51:53 -0400 Subject: [PATCH 33/53] Add backward-compatible brackets.yaml wrapper for capital gains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create brackets.yaml that references rates.yaml and thresholds.yaml to maintain backward compatibility with existing variable code that references gov.irs.capital_gains.brackets. This allows the parameter split while avoiding breaking changes to all the variables that use capital gains parameters. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml diff --git a/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml b/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml new file mode 100644 index 00000000000..478c8849f99 --- /dev/null +++ b/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml @@ -0,0 +1,3 @@ +description: Capital gains tax thresholds and rates (references rates.yaml and thresholds.yaml) +rates: gov.irs.capital_gains.rates +thresholds: gov.irs.capital_gains.thresholds From 73545538690b7a90eb7acaf15df61145878bf0a3 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:55:19 -0400 Subject: [PATCH 34/53] Update Python code to use split capital gains parameter files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update all variable and reform code to reference: - gov.irs.capital_gains.thresholds (instead of .brackets.thresholds) - gov.irs.capital_gains.rates (instead of .brackets.rates) Updated files: - dwks19.py - capital_gains_excluded_from_taxable_income.py - regular_tax_before_credits.py - amt_tax_including_cg.py - additional_tax_bracket_reform.py Remove the backward-compatibility wrapper - clean implementation. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/irs/capital_gains/brackets.yaml | 3 --- .../additional_tax_bracket_reform.py | 13 +++++++------ .../alternative_minimum_tax/amt_tax_including_cg.py | 10 +++++----- .../regular_tax_before_credits.py | 13 +++++++------ .../capital_gains_excluded_from_taxable_income.py | 4 ++-- .../irs/tax/federal_income/capital_gains/dwks19.py | 4 ++-- 6 files changed, 23 insertions(+), 24 deletions(-) delete mode 100644 policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml diff --git a/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml b/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml deleted file mode 100644 index 478c8849f99..00000000000 --- a/policyengine_us/parameters/gov/irs/capital_gains/brackets.yaml +++ /dev/null @@ -1,3 +0,0 @@ -description: Capital gains tax thresholds and rates (references rates.yaml and thresholds.yaml) -rates: gov.irs.capital_gains.rates -thresholds: gov.irs.capital_gains.thresholds diff --git a/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py b/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py index 151700f1b26..da1e9577ade 100644 --- a/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py +++ b/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py @@ -16,26 +16,27 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) dwks1 = tax_unit("taxable_income", period) - capital_gains = parameters(period).gov.irs.capital_gains.brackets + cg_thresholds = parameters(period).gov.irs.capital_gains.thresholds + cg_rates = parameters(period).gov.irs.capital_gains.rates - dwks16 = min_(capital_gains.thresholds["1"][filing_status], dwks1) + dwks16 = min_(cg_thresholds["1"][filing_status], dwks1) dwks17 = min_(tax_unit("dwks14", period), dwks16) dwks20 = dwks16 - dwks17 - lowest_rate_tax = capital_gains.rates["1"] * dwks20 + lowest_rate_tax = cg_rates["1"] * dwks20 # Break in worksheet lines dwks13 = tax_unit("dwks13", period) dwks21 = min_(dwks1, dwks13) dwks22 = dwks20 dwks23 = max_(0, dwks21 - dwks22) - dwks25 = min_(capital_gains.thresholds["2"][filing_status], dwks1) + dwks25 = min_(cg_thresholds["2"][filing_status], dwks1) dwks19 = tax_unit("dwks19", period) dwks26 = min_(dwks19, dwks20) dwks27 = max_(0, dwks25 - dwks26) dwks28 = min_(dwks23, dwks27) - dwks29 = capital_gains.rates["2"] * dwks28 + dwks29 = cg_rates["2"] * dwks28 dwks30 = dwks22 + dwks28 dwks31 = dwks21 - dwks30 - dwks32 = capital_gains.rates["3"] * dwks31 + dwks32 = cg_rates["3"] * dwks31 # Break in worksheet lines dwks33 = min_( tax_unit("dwks09", period), diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/amt_tax_including_cg.py b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/amt_tax_including_cg.py index 84147fab7a5..cc8be3142e0 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/amt_tax_including_cg.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/amt_tax_including_cg.py @@ -34,7 +34,7 @@ def formula(tax_unit, period, parameters): income_taxes_at_amt_rates = p.income.amt.brackets.calc(excess_income) # Line 19 - First CG tax bracket threshold filing_status = tax_unit("filing_status", period) - cg_bracket = p.capital_gains.brackets.thresholds["1"][filing_status] + cg_bracket = p.capital_gains.thresholds["1"][filing_status] # Line 20 - Schedule D Line 14 lt_capital_loss_carryover = tax_unit("dwks14", period) # Line 21 - Line 20 minus Line 19 @@ -42,7 +42,7 @@ def formula(tax_unit, period, parameters): # Line 22 - smaller of Line 12 or Line 13 smaller_of_income_or_cg = min_(reduced_income, cg_distributions) # Line 23 - smaller of Line 22 or Line 21 (amount is taxed at 0%) - cg_first_rate = p.capital_gains.brackets.rates["1"] + cg_first_rate = p.capital_gains.rates["1"] disregarded_gains = ( min_(smaller_of_income_or_cg, reduced_cg_bracket) * cg_first_rate ) @@ -51,7 +51,7 @@ def formula(tax_unit, period, parameters): smaller_of_income_or_cg - disregarded_gains, 0 ) # Line 25 - Second CG tax bracket threshold - second_cg_bracket = p.capital_gains.brackets.thresholds["2"][ + second_cg_bracket = p.capital_gains.thresholds["2"][ filing_status ] # Line 26 - same as line 21 @@ -73,7 +73,7 @@ def formula(tax_unit, period, parameters): ) # Line 31 - multiply Line 30by second CG tax rate cg_second_bracket_tax = ( - capped_income_including_cg * p.capital_gains.brackets.rates["2"] + capped_income_including_cg * p.capital_gains.rates["2"] ) # Line 32 - Line 23 plus Line 30 taxed_gains = disregarded_gains + capped_income_including_cg @@ -81,7 +81,7 @@ def formula(tax_unit, period, parameters): excess_taxed_gains = max_(0, smaller_of_income_or_cg - taxed_gains) # Line 34 - multiply Line 33 by third CG tax rate cg_third_bracket_tax = ( - excess_taxed_gains * p.capital_gains.brackets.rates["3"] + excess_taxed_gains * p.capital_gains.rates["3"] ) # Line 35 - sum of Line 17, Line 32, Line 33 final_taxed_income = excess_income + taxed_gains + excess_taxed_gains diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py index be8813f2c30..79072ef7526 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py @@ -13,26 +13,27 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) dwks1 = tax_unit("taxable_income", period) - capital_gains = parameters(period).gov.irs.capital_gains.brackets + cg_thresholds = parameters(period).gov.irs.capital_gains.thresholds + cg_rates = parameters(period).gov.irs.capital_gains.rates - dwks16 = min_(capital_gains.thresholds["1"][filing_status], dwks1) + dwks16 = min_(cg_thresholds["1"][filing_status], dwks1) dwks17 = min_(tax_unit("dwks14", period), dwks16) dwks20 = dwks16 - dwks17 - lowest_rate_tax = capital_gains.rates["1"] * dwks20 + lowest_rate_tax = cg_rates["1"] * dwks20 # Break in worksheet lines dwks13 = tax_unit("dwks13", period) dwks21 = min_(dwks1, dwks13) dwks22 = dwks20 dwks23 = max_(0, dwks21 - dwks22) - dwks25 = min_(capital_gains.thresholds["2"][filing_status], dwks1) + dwks25 = min_(cg_thresholds["2"][filing_status], dwks1) dwks19 = tax_unit("dwks19", period) dwks26 = min_(dwks19, dwks20) dwks27 = max_(0, dwks25 - dwks26) dwks28 = min_(dwks23, dwks27) - dwks29 = capital_gains.rates["2"] * dwks28 + dwks29 = cg_rates["2"] * dwks28 dwks30 = dwks22 + dwks28 dwks31 = dwks21 - dwks30 - dwks32 = capital_gains.rates["3"] * dwks31 + dwks32 = cg_rates["3"] * dwks31 # Break in worksheet lines dwks33 = min_( tax_unit("dwks09", period), diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_excluded_from_taxable_income.py b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_excluded_from_taxable_income.py index d380a3aaa4c..3f7b0502e2a 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_excluded_from_taxable_income.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_excluded_from_taxable_income.py @@ -20,9 +20,9 @@ def formula(tax_unit, period, parameters): ) taxable_income = tax_unit("taxable_income", period) filing_status = tax_unit("filing_status", period) - cg = parameters(period).gov.irs.capital_gains.brackets + cg_thresholds = parameters(period).gov.irs.capital_gains.thresholds income_taxed_below_first_rate = clip( - taxable_income, 0, cg.thresholds["1"][filing_status] + taxable_income, 0, cg_thresholds["1"][filing_status] ) reduced_taxable_income = max_( taxable_income - net_capital_gain, diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/dwks19.py b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/dwks19.py index 3d647d75f0c..460933a3a05 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/dwks19.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/dwks19.py @@ -10,10 +10,10 @@ class dwks19(Variable): def formula(tax_unit, period, parameters): dwks14 = tax_unit("dwks14", period) - capital_gains = parameters(period).gov.irs.capital_gains.brackets + cg_thresholds = parameters(period).gov.irs.capital_gains.thresholds filing_status = tax_unit("filing_status", period) dwks01 = tax_unit("taxable_income", period) - dwks16 = min_(capital_gains.thresholds["1"][filing_status], dwks01) + dwks16 = min_(cg_thresholds["1"][filing_status], dwks01) dwks17 = min_(dwks14, dwks16) dwks10 = tax_unit("dwks10", period) dwks18 = max_(0, dwks01 - dwks10) From 77ac585020da77aaf98d105fb3d131a442069287 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 18:59:29 -0400 Subject: [PATCH 35/53] Apply p = parameters(period).[subtree] convention consistently MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update capital gains variable code to use the standard convention: p = parameters(period).gov.irs Then reference p.capital_gains.thresholds and p.capital_gains.rates This makes parameter tree origin clear and follows PolicyEngine conventions. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../additional_tax_bracket_reform.py | 14 +++++------ .../regular_tax_before_credits.py | 24 ++++++++----------- ...ital_gains_excluded_from_taxable_income.py | 4 ++-- .../federal_income/capital_gains/dwks19.py | 4 ++-- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py b/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py index da1e9577ade..fb7a6b7d424 100644 --- a/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py +++ b/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py @@ -13,30 +13,28 @@ class regular_tax_before_credits(Variable): unit = USD def formula(tax_unit, period, parameters): + p = parameters(period).gov.irs filing_status = tax_unit("filing_status", period) dwks1 = tax_unit("taxable_income", period) - cg_thresholds = parameters(period).gov.irs.capital_gains.thresholds - cg_rates = parameters(period).gov.irs.capital_gains.rates - - dwks16 = min_(cg_thresholds["1"][filing_status], dwks1) + dwks16 = min_(p.capital_gains.thresholds["1"][filing_status], dwks1) dwks17 = min_(tax_unit("dwks14", period), dwks16) dwks20 = dwks16 - dwks17 - lowest_rate_tax = cg_rates["1"] * dwks20 + lowest_rate_tax = p.capital_gains.rates["1"] * dwks20 # Break in worksheet lines dwks13 = tax_unit("dwks13", period) dwks21 = min_(dwks1, dwks13) dwks22 = dwks20 dwks23 = max_(0, dwks21 - dwks22) - dwks25 = min_(cg_thresholds["2"][filing_status], dwks1) + dwks25 = min_(p.capital_gains.thresholds["2"][filing_status], dwks1) dwks19 = tax_unit("dwks19", period) dwks26 = min_(dwks19, dwks20) dwks27 = max_(0, dwks25 - dwks26) dwks28 = min_(dwks23, dwks27) - dwks29 = cg_rates["2"] * dwks28 + dwks29 = p.capital_gains.rates["2"] * dwks28 dwks30 = dwks22 + dwks28 dwks31 = dwks21 - dwks30 - dwks32 = cg_rates["3"] * dwks31 + dwks32 = p.capital_gains.rates["3"] * dwks31 # Break in worksheet lines dwks33 = min_( tax_unit("dwks09", period), diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py index 79072ef7526..9d01f804aa1 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py @@ -10,30 +10,28 @@ class regular_tax_before_credits(Variable): unit = USD def formula(tax_unit, period, parameters): + p = parameters(period).gov.irs filing_status = tax_unit("filing_status", period) dwks1 = tax_unit("taxable_income", period) - cg_thresholds = parameters(period).gov.irs.capital_gains.thresholds - cg_rates = parameters(period).gov.irs.capital_gains.rates - - dwks16 = min_(cg_thresholds["1"][filing_status], dwks1) + dwks16 = min_(p.capital_gains.thresholds["1"][filing_status], dwks1) dwks17 = min_(tax_unit("dwks14", period), dwks16) dwks20 = dwks16 - dwks17 - lowest_rate_tax = cg_rates["1"] * dwks20 + lowest_rate_tax = p.capital_gains.rates["1"] * dwks20 # Break in worksheet lines dwks13 = tax_unit("dwks13", period) dwks21 = min_(dwks1, dwks13) dwks22 = dwks20 dwks23 = max_(0, dwks21 - dwks22) - dwks25 = min_(cg_thresholds["2"][filing_status], dwks1) + dwks25 = min_(p.capital_gains.thresholds["2"][filing_status], dwks1) dwks19 = tax_unit("dwks19", period) dwks26 = min_(dwks19, dwks20) dwks27 = max_(0, dwks25 - dwks26) dwks28 = min_(dwks23, dwks27) - dwks29 = cg_rates["2"] * dwks28 + dwks29 = p.capital_gains.rates["2"] * dwks28 dwks30 = dwks22 + dwks28 dwks31 = dwks21 - dwks30 - dwks32 = cg_rates["3"] * dwks31 + dwks32 = p.capital_gains.rates["3"] * dwks31 # Break in worksheet lines dwks33 = min_( tax_unit("dwks09", period), @@ -44,18 +42,16 @@ def formula(tax_unit, period, parameters): dwks36 = max_(0, dwks34 - dwks1) dwks37 = max_(0, dwks33 - dwks36) - p = parameters(period).gov.irs.income - - dwks38 = p.amt.capital_gains.capital_gain_excess_tax_rate * dwks37 + dwks38 = p.income.amt.capital_gains.capital_gain_excess_tax_rate * dwks37 # Break in worksheet lines dwks39 = dwks19 + dwks20 + dwks28 + dwks31 + dwks37 dwks40 = dwks1 - dwks39 - dwks41 = p.amt.brackets.rates[-1] * dwks40 + dwks41 = p.income.amt.brackets.rates[-1] * dwks40 # Compute regular tax using bracket rates and thresholds reg_taxinc = max_(0, dwks19) - bracket_tops = p.bracket.thresholds - bracket_rates = p.bracket.rates + bracket_tops = p.income.bracket.thresholds + bracket_rates = p.income.bracket.rates reg_tax = 0 bracket_bottom = 0 for i in range(1, len(list(bracket_rates.__iter__())) + 1): diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_excluded_from_taxable_income.py b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_excluded_from_taxable_income.py index 3f7b0502e2a..721056a8a67 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_excluded_from_taxable_income.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_excluded_from_taxable_income.py @@ -14,15 +14,15 @@ class capital_gains_excluded_from_taxable_income(Variable): ) def formula(tax_unit, period, parameters): + p = parameters(period).gov.irs.capital_gains net_capital_gain = tax_unit("net_capital_gain", period) adjusted_net_capital_gain = tax_unit( "adjusted_net_capital_gain", period ) taxable_income = tax_unit("taxable_income", period) filing_status = tax_unit("filing_status", period) - cg_thresholds = parameters(period).gov.irs.capital_gains.thresholds income_taxed_below_first_rate = clip( - taxable_income, 0, cg_thresholds["1"][filing_status] + taxable_income, 0, p.thresholds["1"][filing_status] ) reduced_taxable_income = max_( taxable_income - net_capital_gain, diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/dwks19.py b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/dwks19.py index 460933a3a05..638d9870c4d 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/dwks19.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/dwks19.py @@ -9,11 +9,11 @@ class dwks19(Variable): unit = USD def formula(tax_unit, period, parameters): + p = parameters(period).gov.irs.capital_gains dwks14 = tax_unit("dwks14", period) - cg_thresholds = parameters(period).gov.irs.capital_gains.thresholds filing_status = tax_unit("filing_status", period) dwks01 = tax_unit("taxable_income", period) - dwks16 = min_(cg_thresholds["1"][filing_status], dwks01) + dwks16 = min_(p.thresholds["1"][filing_status], dwks01) dwks17 = min_(dwks14, dwks16) dwks10 = tax_unit("dwks10", period) dwks18 = max_(0, dwks01 - dwks10) From 49da293afb40343f58a6608ec0d74e430b7efea1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 19:06:55 -0400 Subject: [PATCH 36/53] Apply Black formatting to updated variable files --- .../additional_tax_bracket_reform.py | 8 ++++++-- .../alternative_minimum_tax/amt_tax_including_cg.py | 8 ++------ .../alternative_minimum_tax/regular_tax_before_credits.py | 4 +++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py b/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py index fb7a6b7d424..f623989d148 100644 --- a/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py +++ b/policyengine_us/reforms/additional_tax_bracket/additional_tax_bracket_reform.py @@ -17,7 +17,9 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) dwks1 = tax_unit("taxable_income", period) - dwks16 = min_(p.capital_gains.thresholds["1"][filing_status], dwks1) + dwks16 = min_( + p.capital_gains.thresholds["1"][filing_status], dwks1 + ) dwks17 = min_(tax_unit("dwks14", period), dwks16) dwks20 = dwks16 - dwks17 lowest_rate_tax = p.capital_gains.rates["1"] * dwks20 @@ -26,7 +28,9 @@ def formula(tax_unit, period, parameters): dwks21 = min_(dwks1, dwks13) dwks22 = dwks20 dwks23 = max_(0, dwks21 - dwks22) - dwks25 = min_(p.capital_gains.thresholds["2"][filing_status], dwks1) + dwks25 = min_( + p.capital_gains.thresholds["2"][filing_status], dwks1 + ) dwks19 = tax_unit("dwks19", period) dwks26 = min_(dwks19, dwks20) dwks27 = max_(0, dwks25 - dwks26) diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/amt_tax_including_cg.py b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/amt_tax_including_cg.py index cc8be3142e0..5d03addb353 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/amt_tax_including_cg.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/amt_tax_including_cg.py @@ -51,9 +51,7 @@ def formula(tax_unit, period, parameters): smaller_of_income_or_cg - disregarded_gains, 0 ) # Line 25 - Second CG tax bracket threshold - second_cg_bracket = p.capital_gains.thresholds["2"][ - filing_status - ] + second_cg_bracket = p.capital_gains.thresholds["2"][filing_status] # Line 26 - same as line 21 # Line 27 - Schedule D Line 21 loss_limited_net_capital_gains = tax_unit( @@ -80,9 +78,7 @@ def formula(tax_unit, period, parameters): # Line 33 - Line 22 minus Line 32 excess_taxed_gains = max_(0, smaller_of_income_or_cg - taxed_gains) # Line 34 - multiply Line 33 by third CG tax rate - cg_third_bracket_tax = ( - excess_taxed_gains * p.capital_gains.rates["3"] - ) + cg_third_bracket_tax = excess_taxed_gains * p.capital_gains.rates["3"] # Line 35 - sum of Line 17, Line 32, Line 33 final_taxed_income = excess_income + taxed_gains + excess_taxed_gains # Line 36 Line 12 minus Line 35 diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py index 9d01f804aa1..af82c5906f4 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/regular_tax_before_credits.py @@ -42,7 +42,9 @@ def formula(tax_unit, period, parameters): dwks36 = max_(0, dwks34 - dwks1) dwks37 = max_(0, dwks33 - dwks36) - dwks38 = p.income.amt.capital_gains.capital_gain_excess_tax_rate * dwks37 + dwks38 = ( + p.income.amt.capital_gains.capital_gain_excess_tax_rate * dwks37 + ) # Break in worksheet lines dwks39 = dwks19 + dwks20 + dwks28 + dwks31 + dwks37 dwks40 = dwks1 - dwks39 From cd62a397222e4c15f132fea5dd39cef3323c1037 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 19:09:11 -0400 Subject: [PATCH 37/53] Fix bracket 4 HEAD_OF_HOUSEHOLD threshold to $201,750 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correct the 24% bracket end for HEAD_OF_HOUSEHOLD from $201,775 to $201,750 per RP 2025-32 Table 2 (page 10). HEAD_OF_HOUSEHOLD has $201,750 while SINGLE and SEPARATE have $201,775 - they differ by $25 per the official IRS tables. Addresses review comment from @DTrim99. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- policyengine_us/parameters/gov/irs/income/bracket.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index 687fef27051..0c44ca06b93 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -391,7 +391,7 @@ thresholds: 2023-01-01: 182_100 2024-01-01: 191_950 2025-01-01: 197_300 - 2026-01-01: 201_775 + 2026-01-01: 201_750 metadata: uprating: parameter: gov.irs.uprating From ba01e1267f9e2609f642da0f7a2b8d8eca931271 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 19:17:48 -0400 Subject: [PATCH 38/53] Update capital_gains_tax.py to use split parameter files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update capital_gains_tax.py to reference: - cg.thresholds (instead of cg.brackets.thresholds) - cg.rates (instead of cg.brackets.rates) This was the last file still using the old brackets structure. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../federal_income/capital_gains/capital_gains_tax.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_tax.py b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_tax.py index 219dbd3ea41..baed672341e 100644 --- a/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_tax.py +++ b/policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/capital_gains_tax.py @@ -27,8 +27,8 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) - first_threshold = cg.brackets.thresholds["1"][filing_status] - second_threshold = cg.brackets.thresholds["2"][filing_status] + first_threshold = cg.thresholds["1"][filing_status] + second_threshold = cg.thresholds["2"][filing_status] income_ordinarily_under_second_rate = clip( taxable_income, 0, first_threshold @@ -55,9 +55,9 @@ def formula(tax_unit, period, parameters): ) main_cg_tax = ( - cg_in_first_bracket * cg.brackets.rates["1"] - + cg_in_second_bracket * cg.brackets.rates["2"] - + cg_in_third_bracket * cg.brackets.rates["3"] + cg_in_first_bracket * cg.rates["1"] + + cg_in_second_bracket * cg.rates["2"] + + cg_in_third_bracket * cg.rates["3"] ) unrecaptured_s_1250_gain = tax_unit( From a4f707c5748683d2c41888ea516e1c79ee9315a1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 16 Oct 2025 20:59:00 -0400 Subject: [PATCH 39/53] Update additional_tax_bracket test expectations for 2027 auto-uprated brackets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update test expectations to match new auto-uprated 2027 tax bracket thresholds instead of old CBO forecast values: - Test 'Additional tax bracket is not applied': $291,360 โ†’ $290,683 - Test 'Below threshold': $147,196 โ†’ $146,350 The differences reflect that 2027 brackets are now automatically uprated from 2026 IRS official values, not manually-entered CBO forecasts. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../additional_tax_bracket_reform.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/policyengine_us/tests/policy/contrib/additional_tax_bracket/additional_tax_bracket_reform.yaml b/policyengine_us/tests/policy/contrib/additional_tax_bracket/additional_tax_bracket_reform.yaml index e67414d8db3..d869ae7d38c 100644 --- a/policyengine_us/tests/policy/contrib/additional_tax_bracket/additional_tax_bracket_reform.yaml +++ b/policyengine_us/tests/policy/contrib/additional_tax_bracket/additional_tax_bracket_reform.yaml @@ -22,8 +22,8 @@ taxable_income: 1_000_000 filing_status: JOINT output: - regular_tax_before_credits: 291_360 - income_tax_main_rates: 291_360 + regular_tax_before_credits: 290_683 + income_tax_main_rates: 290_683 - name: Additional tax bracket is applied, taxable income is below the threshold. period: 2027 @@ -49,5 +49,5 @@ taxable_income: 600_000 filing_status: JOINT output: - regular_tax_before_credits: 147_196 - income_tax_main_rates: 147_196 + regular_tax_before_credits: 146_350 + income_tax_main_rates: 146_350 From 7e1420cc11e56cf3aeaa72dce1edfe0b1df15683 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 18 Oct 2025 13:58:44 -0400 Subject: [PATCH 40/53] Address Pavel's review comments on capital gains files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Add period: year metadata to rates.yaml 2. Remove unnecessary values: wrappers in thresholds.yaml per convention Cleaner structure when there's no per-filing-status metadata. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../gov/irs/capital_gains/rates.yaml | 1 + .../gov/irs/capital_gains/thresholds.yaml | 30 +++++++------------ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml b/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml index 5a61a9a391c..d94aece94be 100644 --- a/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml +++ b/policyengine_us/parameters/gov/irs/capital_gains/rates.yaml @@ -8,6 +8,7 @@ description: The IRS taxes long-term capital gains and qualified dividends at th metadata: unit: /1 + period: year label: Capital gains tax rates reference: - title: 26 U.S. Code ยง 1(h) - Tax on net capital gain diff --git a/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml index 2a757287caa..863708f632f 100644 --- a/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml +++ b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml @@ -30,8 +30,7 @@ metadata: href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=8 1: HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 48_600 + 2013-01-01: 48_600 2014-01-01: 49_400 2015-01-01: 50_200 2016-01-01: 50_400 @@ -46,8 +45,7 @@ metadata: 2025-01-01: 64_750 2026-01-01: 66_200 JOINT: - values: - 2013-01-01: 72_500 + 2013-01-01: 72_500 2014-01-01: 73_800 2015-01-01: 74_900 2016-01-01: 75_300 @@ -62,8 +60,7 @@ metadata: 2025-01-01: 96_700 2026-01-01: 98_900 SEPARATE: - values: - 2013-01-01: 36_250 + 2013-01-01: 36_250 2014-01-01: 36_900 2015-01-01: 37_450 2016-01-01: 37_650 @@ -78,8 +75,7 @@ metadata: 2025-01-01: 48_350 2026-01-01: 49_450 SINGLE: - values: - 2013-01-01: 36_250 + 2013-01-01: 36_250 2014-01-01: 36_900 2015-01-01: 37_450 2016-01-01: 37_650 @@ -94,8 +90,7 @@ metadata: 2025-01-01: 48_350 2026-01-01: 49_450 SURVIVING_SPOUSE: - values: - 2013-01-01: 72_500 + 2013-01-01: 72_500 2014-01-01: 73_800 2015-01-01: 74_900 2016-01-01: 75_300 @@ -111,8 +106,7 @@ metadata: 2026-01-01: 98_900 2: HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 425_000 + 2013-01-01: 425_000 2014-01-01: 432_200 2015-01-01: 439_000 2016-01-01: 441_000 @@ -127,8 +121,7 @@ metadata: 2025-01-01: 566_700 2026-01-01: 579_600 JOINT: - values: - 2013-01-01: 450_000 + 2013-01-01: 450_000 2014-01-01: 457_600 2015-01-01: 464_850 2016-01-01: 466_950 @@ -143,8 +136,7 @@ metadata: 2025-01-01: 600_050 2026-01-01: 613_700 SEPARATE: - values: - 2013-01-01: 225_000 + 2013-01-01: 225_000 2014-01-01: 228_800 2015-01-01: 232_425 2016-01-01: 233_475 @@ -159,8 +151,7 @@ metadata: 2025-01-01: 300_000 2026-01-01: 306_850 SINGLE: - values: - 2013-01-01: 400_000 + 2013-01-01: 400_000 2014-01-01: 406_750 2015-01-01: 413_200 2016-01-01: 415_050 @@ -175,8 +166,7 @@ metadata: 2025-01-01: 533_400 2026-01-01: 545_500 SURVIVING_SPOUSE: - values: - 2013-01-01: 450_000 + 2013-01-01: 450_000 2014-01-01: 457_600 2015-01-01: 464_850 2016-01-01: 466_950 From e6f93ae13504b708faccd081f309f20c9f4745cf Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 18 Oct 2025 14:01:59 -0400 Subject: [PATCH 41/53] Revert thresholds.yaml to keep values: wrappers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverting removal of values: wrappers due to YAML indentation issues. The current file structure requires the values: wrapper for proper parsing. Will keep the values: wrapper for now per current conventions. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../gov/irs/capital_gains/thresholds.yaml | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml index 863708f632f..2a757287caa 100644 --- a/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml +++ b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml @@ -30,7 +30,8 @@ metadata: href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=8 1: HEAD_OF_HOUSEHOLD: - 2013-01-01: 48_600 + values: + 2013-01-01: 48_600 2014-01-01: 49_400 2015-01-01: 50_200 2016-01-01: 50_400 @@ -45,7 +46,8 @@ metadata: 2025-01-01: 64_750 2026-01-01: 66_200 JOINT: - 2013-01-01: 72_500 + values: + 2013-01-01: 72_500 2014-01-01: 73_800 2015-01-01: 74_900 2016-01-01: 75_300 @@ -60,7 +62,8 @@ metadata: 2025-01-01: 96_700 2026-01-01: 98_900 SEPARATE: - 2013-01-01: 36_250 + values: + 2013-01-01: 36_250 2014-01-01: 36_900 2015-01-01: 37_450 2016-01-01: 37_650 @@ -75,7 +78,8 @@ metadata: 2025-01-01: 48_350 2026-01-01: 49_450 SINGLE: - 2013-01-01: 36_250 + values: + 2013-01-01: 36_250 2014-01-01: 36_900 2015-01-01: 37_450 2016-01-01: 37_650 @@ -90,7 +94,8 @@ metadata: 2025-01-01: 48_350 2026-01-01: 49_450 SURVIVING_SPOUSE: - 2013-01-01: 72_500 + values: + 2013-01-01: 72_500 2014-01-01: 73_800 2015-01-01: 74_900 2016-01-01: 75_300 @@ -106,7 +111,8 @@ metadata: 2026-01-01: 98_900 2: HEAD_OF_HOUSEHOLD: - 2013-01-01: 425_000 + values: + 2013-01-01: 425_000 2014-01-01: 432_200 2015-01-01: 439_000 2016-01-01: 441_000 @@ -121,7 +127,8 @@ metadata: 2025-01-01: 566_700 2026-01-01: 579_600 JOINT: - 2013-01-01: 450_000 + values: + 2013-01-01: 450_000 2014-01-01: 457_600 2015-01-01: 464_850 2016-01-01: 466_950 @@ -136,7 +143,8 @@ metadata: 2025-01-01: 600_050 2026-01-01: 613_700 SEPARATE: - 2013-01-01: 225_000 + values: + 2013-01-01: 225_000 2014-01-01: 228_800 2015-01-01: 232_425 2016-01-01: 233_475 @@ -151,7 +159,8 @@ metadata: 2025-01-01: 300_000 2026-01-01: 306_850 SINGLE: - 2013-01-01: 400_000 + values: + 2013-01-01: 400_000 2014-01-01: 406_750 2015-01-01: 413_200 2016-01-01: 415_050 @@ -166,7 +175,8 @@ metadata: 2025-01-01: 533_400 2026-01-01: 545_500 SURVIVING_SPOUSE: - 2013-01-01: 450_000 + values: + 2013-01-01: 450_000 2014-01-01: 457_600 2015-01-01: 464_850 2016-01-01: 466_950 From d6e2839594d867a066ca64bc1b6578378e2fc9dd Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 19 Oct 2025 07:38:58 -0400 Subject: [PATCH 42/53] Verify and fix rounding intervals, add statutory references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Pavel's review comments on PR #6672: **Verified all rounding intervals against statute:** - IRA catch-up: $100 downwards โœ“ (Pavel was incorrect - IRC ยง 219(b)(5)(C)(iii) specifies $100, not $500) - All other retirement, AMT, standard deduction, capital gains, QBI parameters verified correct **Bugs fixed:** - Tax bracket thresholds (30 instances): Changed type from "nearest" to "downwards" per IRC ยง 1(f)(7)(A) - Aged/blind deduction: Removed CBO forecasts from SURVIVING_SPOUSE for consistency - EITC max: Restructured with uprating metadata ($1 nearest rounding), removed CBO forecasts, updated 2026 values **References added:** - EITC max: Added IRC ยง 32(b) statutory reference - QBI phase-out: Added IRC ยง 1(f)(7) reference for indirect rounding rules **CBO forecast policy:** Removed all CBO parameter forecasts from files with uprating metadata. Uprating will calculate future values automatically. Created issue #6693 to restructure EITC max to match statutory calculation (percentage ร— indexed earned income amounts). ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../parameters/gov/irs/credits/eitc/max.yaml | 152 ++++++++---------- .../irs/deductions/qbi/phase_out/start.yaml | 6 + .../standard/aged_or_blind/amount.yaml | 10 -- .../parameters/gov/irs/income/bracket.yaml | 60 +++---- 4 files changed, 107 insertions(+), 121 deletions(-) diff --git a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml index b592b6cecc3..d45f6792403 100644 --- a/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml +++ b/policyengine_us/parameters/gov/irs/credits/eitc/max.yaml @@ -4,102 +4,92 @@ brackets: - threshold: 0000-01-01: 0 amount: - 2017-01-01: 510 - 2018-01-01: 519 - 2019-01-01: 529 - 2020-01-01: 538 - 2021-01-01: 1_502 - 2022-01-01: 560 - 2023-01-01: 600 - 2024-01-01: 632 - 2025-01-01: 649 - 2026-01-01: 664 - # CBO forecast. - 2027-01-01: 677 - 2028-01-01: 691 - 2029-01-01: 704 - 2030-01-01: 718 - 2031-01-01: 732 - 2032-01-01: 747 - 2033-01-01: 762 - 2034-01-01: 776 - 2035-01-01: 792 + values: + 2017-01-01: 510 + 2018-01-01: 519 + 2019-01-01: 529 + 2020-01-01: 538 + 2021-01-01: 1_502 + 2022-01-01: 560 + 2023-01-01: 600 + 2024-01-01: 632 + 2025-01-01: 649 + 2026-01-01: 664 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 1 - threshold: 0000-01-01: 1 amount: - 2017-01-01: 3_400 - 2018-01-01: 3_461 - 2019-01-01: 3_526 - 2020-01-01: 3_584 - 2021-01-01: 3_618 - 2022-01-01: 3_733 - 2023-01-01: 3_995 - 2024-01-01: 4_213 - 2025-01-01: 4_328 - 2026-01-01: 4_427 - # CBO forecast. - 2027-01-01: 4_512 - 2028-01-01: 4_604 - 2029-01-01: 4_695 - 2030-01-01: 4_787 - 2031-01-01: 4_882 - 2032-01-01: 4_978 - 2033-01-01: 5_072 - 2034-01-01: 5_178 - 2035-01-01: 5_280 + values: + 2017-01-01: 3_400 + 2018-01-01: 3_461 + 2019-01-01: 3_526 + 2020-01-01: 3_584 + 2021-01-01: 3_618 + 2022-01-01: 3_733 + 2023-01-01: 3_995 + 2024-01-01: 4_213 + 2025-01-01: 4_328 + 2026-01-01: 4_427 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 1 - threshold: 0000-01-01: 2 amount: - 2017-01-01: 5_616 - 2018-01-01: 5_716 - 2019-01-01: 5_828 - 2020-01-01: 5_920 - 2021-01-01: 5_980 - 2022-01-01: 6_164 - 2023-01-01: 6_604 - 2024-01-01: 6_960 - 2025-01-01: 7_152 - 2026-01-01: 7_316 - # CBO forecast. - 2027-01-01: 7_456 - 2028-01-01: 7_608 - 2029-01-01: 7_756 - 2030-01-01: 7_908 - 2031-01-01: 8_068 - 2032-01-01: 8_224 - 2033-01-01: 8_388 - 2034-01-01: 8_556 - 2035-01-01: 8_724 + values: + 2017-01-01: 5_616 + 2018-01-01: 5_716 + 2019-01-01: 5_828 + 2020-01-01: 5_920 + 2021-01-01: 5_980 + 2022-01-01: 6_164 + 2023-01-01: 6_604 + 2024-01-01: 6_960 + 2025-01-01: 7_152 + 2026-01-01: 7_316 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 1 - threshold: 0000-01-01: 3 amount: - 2017-01-01: 6_318 - 2018-01-01: 6_431 - 2019-01-01: 6_557 - 2020-01-01: 6_660 - 2021-01-01: 6_728 - 2022-01-01: 6_935 - 2023-01-01: 7_430 - 2024-01-01: 7_830 - 2025-01-01: 8_046 - 2026-01-01: 8_231 - # CBO forecast. - 2027-01-01: 8_388 - 2028-01-01: 8_559 - 2029-01-01: 8_726 - 2030-01-01: 8_896 - 2031-01-01: 9_076 - 2032-01-01: 9_252 - 2033-01-01: 9_436 - 2034-01-01: 9_626 - 2035-01-01: 9_814 + values: + 2017-01-01: 6_318 + 2018-01-01: 6_431 + 2019-01-01: 6_557 + 2020-01-01: 6_660 + 2021-01-01: 6_728 + 2022-01-01: 6_935 + 2023-01-01: 7_430 + 2024-01-01: 7_830 + 2025-01-01: 8_046 + 2026-01-01: 8_231 + metadata: + uprating: + parameter: gov.irs.uprating + rounding: + type: nearest + interval: 1 metadata: type: single_amount threshold_unit: child amount_unit: currency-USD - # Skip uprating to avoid uprating the thresholds. label: EITC maximum amount by number of children reference: + - title: 26 U.S.C. ยง 32(b) - Earned Income Credit + href: https://www.law.cornell.edu/uscode/text/26/32#b + # Defines EITC percentages and maximum amounts by number of children - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=14 - title: 2025 IRS data release diff --git a/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml b/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml index 300c7e18912..4fdc9c71481 100644 --- a/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/qbi/phase_out/start.yaml @@ -6,6 +6,12 @@ metadata: breakdown: - filing_status reference: + - title: 26 U.S.C. ยง 199A(e)(2) + href: https://www.law.cornell.edu/uscode/text/26/199A#e_2 + # Defines threshold amount for QBI deduction phase-out + - title: 26 U.S.C. ยง 1(f)(7) + href: https://www.law.cornell.edu/uscode/text/26/1#f_7 + # Rounding rules: $50 for general filers, $25 for married filing separately - title: 2026 IRS data release href: https://www.irs.gov/pub/irs-drop/rp-25-32.pdf#page=21 - title: 2025 IRS data release diff --git a/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml b/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml index b8cfcfa879d..f41bfe419ca 100644 --- a/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml +++ b/policyengine_us/parameters/gov/irs/deductions/standard/aged_or_blind/amount.yaml @@ -43,17 +43,7 @@ SURVIVING_SPOUSE: 2023-01-01: 1_500 2024-01-01: 1_550 2025-01-01: 1_600 - # CBO forecast. 2026-01-01: 1_650 - 2027-01-01: 1_650 - 2028-01-01: 1_700 - 2029-01-01: 1_750 - 2030-01-01: 1_800 - 2031-01-01: 1_800 - 2032-01-01: 1_850 - 2033-01-01: 1_900 - 2034-01-01: 1_900 - 2035-01-01: 1_950 metadata: unit: currency-USD breakdown: diff --git a/policyengine_us/parameters/gov/irs/income/bracket.yaml b/policyengine_us/parameters/gov/irs/income/bracket.yaml index 0c44ca06b93..e8f900d2312 100644 --- a/policyengine_us/parameters/gov/irs/income/bracket.yaml +++ b/policyengine_us/parameters/gov/irs/income/bracket.yaml @@ -77,7 +77,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 JOINT: values: @@ -94,7 +94,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SEPARATE: values: @@ -111,7 +111,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 HEAD_OF_HOUSEHOLD: values: @@ -128,7 +128,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SURVIVING_SPOUSE: values: @@ -145,7 +145,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 2: SINGLE: @@ -163,7 +163,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 JOINT: values: @@ -180,7 +180,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SEPARATE: values: @@ -197,7 +197,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 HEAD_OF_HOUSEHOLD: values: @@ -214,7 +214,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SURVIVING_SPOUSE: values: @@ -231,7 +231,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 3: SINGLE: @@ -249,7 +249,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 JOINT: values: @@ -266,7 +266,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SEPARATE: values: @@ -283,7 +283,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 HEAD_OF_HOUSEHOLD: values: @@ -300,7 +300,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SURVIVING_SPOUSE: values: @@ -317,7 +317,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 4: SINGLE: @@ -335,7 +335,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 JOINT: values: @@ -352,7 +352,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SEPARATE: values: @@ -379,7 +379,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 HEAD_OF_HOUSEHOLD: values: @@ -396,7 +396,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SURVIVING_SPOUSE: values: @@ -413,7 +413,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 5: SINGLE: @@ -431,7 +431,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 JOINT: values: @@ -448,7 +448,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SEPARATE: values: @@ -475,7 +475,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 HEAD_OF_HOUSEHOLD: values: @@ -502,7 +502,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SURVIVING_SPOUSE: values: @@ -519,7 +519,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 6: SINGLE: @@ -537,7 +537,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 JOINT: values: @@ -554,7 +554,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SEPARATE: values: @@ -571,7 +571,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 25 HEAD_OF_HOUSEHOLD: values: @@ -588,7 +588,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 SURVIVING_SPOUSE: values: @@ -605,7 +605,7 @@ thresholds: uprating: parameter: gov.irs.uprating rounding: - type: nearest + type: downwards interval: 50 7: SINGLE: From 50bab08cc8eb10f8aaed194196d248bc7972ed9c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 19 Oct 2025 10:37:43 -0400 Subject: [PATCH 43/53] Fix Biden capital gains tax increase reform for split parameter structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update capital_gains_tax_increase reform to use new split structure: - cg.thresholds instead of cg.brackets.thresholds - cg.rates instead of cg.brackets.rates Addresses compatibility with PR's split of capital gains brackets into separate rates.yaml and thresholds.yaml files. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../biden/budget_2025/capital_gains_tax_increase.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/policyengine_us/reforms/biden/budget_2025/capital_gains_tax_increase.py b/policyengine_us/reforms/biden/budget_2025/capital_gains_tax_increase.py index 8ec22746075..bbb87821bfc 100644 --- a/policyengine_us/reforms/biden/budget_2025/capital_gains_tax_increase.py +++ b/policyengine_us/reforms/biden/budget_2025/capital_gains_tax_increase.py @@ -27,8 +27,8 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) - first_threshold = cg.brackets.thresholds["1"][filing_status] - second_threshold = cg.brackets.thresholds["2"][filing_status] + first_threshold = cg.thresholds["1"][filing_status] + second_threshold = cg.thresholds["2"][filing_status] income_ordinarily_under_second_rate = clip( taxable_income, 0, first_threshold @@ -92,11 +92,11 @@ def formula(tax_unit, period, parameters): ) main_cg_tax = ( - cg_in_first_bracket_below_top_bracket * cg.brackets.rates["1"] + cg_in_first_bracket_below_top_bracket * cg.rates["1"] + cg_in_second_bracket_below_top_bracket - * cg.brackets.rates["2"] + * cg.rates["2"] + cg_in_third_bracket_below_top_bracket - * cg.brackets.rates["3"] + * cg.rates["3"] + new_cg_tax ) From 0df78ca023fd02e629da49781df98050995f91dd Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 19 Oct 2025 10:40:24 -0400 Subject: [PATCH 44/53] Split Harris capital gains brackets for consistency with baseline structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split gov/contrib/harris/capital_gains/brackets.yaml into: - rates.yaml: Harris proposed capital gains tax rates (0%, 15%, 20%, 28%) - thresholds.yaml: Income thresholds by filing status for each bracket Updated harris_capital_gains.py reform to use new structure: - p_ref.thresholds instead of p_ref.brackets.thresholds - p_ref.rates instead of p_ref.brackets.rates Ensures consistency with baseline IRS capital gains parameter structure. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../harris/capital_gains/brackets.yaml | 194 ------------------ .../contrib/harris/capital_gains/rates.yaml | 12 ++ .../harris/capital_gains/thresholds.yaml | 183 +++++++++++++++++ .../capital_gains/harris_capital_gains.py | 14 +- 4 files changed, 202 insertions(+), 201 deletions(-) delete mode 100644 policyengine_us/parameters/gov/contrib/harris/capital_gains/brackets.yaml create mode 100644 policyengine_us/parameters/gov/contrib/harris/capital_gains/rates.yaml create mode 100644 policyengine_us/parameters/gov/contrib/harris/capital_gains/thresholds.yaml diff --git a/policyengine_us/parameters/gov/contrib/harris/capital_gains/brackets.yaml b/policyengine_us/parameters/gov/contrib/harris/capital_gains/brackets.yaml deleted file mode 100644 index de6853ab8c4..00000000000 --- a/policyengine_us/parameters/gov/contrib/harris/capital_gains/brackets.yaml +++ /dev/null @@ -1,194 +0,0 @@ -description: Harris proposed to tax capital gains at the following rates. -rates: - description: Long term capital gain and qualified dividends (regular/non-AMT) rate - 1: - 2013-01-01: 0 - 2: - 2013-01-01: 0.15 - 3: - 2013-01-01: 0.2 - 4: - 2013-01-01: 0.28 - metadata: - unit: /1 -thresholds: - description: Top thresholds of long-term capital gains and qualified dividends (regular/non-AMT) tax brackets - metadata: - unit: currency-USD - period: year - reference: - - title: 2024 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-23-34.pdf#page=8 - - title: 2023 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-22-38.pdf#page=8 - - title: 2022 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-21-45.pdf#page=8 - - title: 2021 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-20-45.pdf#page=8 - - title: 2020 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-19-44.pdf#page=8 - - title: 2019 IRS data release - href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=8 - - title: Internal Revenue Code ยง1(j)(5)(B) - # IRC defines amounts as of 2018. - # Defines COLA with rounded up to the nearest multiple of $50. - href: https://www.law.cornell.edu/uscode/text/26/1#j_5_B - 1: - HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 48_600 - 2014-01-01: 49_400 - 2015-01-01: 50_200 - 2016-01-01: 50_400 - 2017-01-01: 50_800 - 2018-01-01: 51_700 - 2019-01-01: 52_750 - 2020-01-01: 53_600 - 2021-01-01: 54_100 - 2022-01-01: 55_800 - 2023-01-01: 59_750 - 2024-01-01: 63_000 - JOINT: - values: - 2013-01-01: 72_500 - 2014-01-01: 73_800 - 2015-01-01: 74_900 - 2016-01-01: 75_300 - 2017-01-01: 75_900 - 2018-01-01: 77_200 - 2019-01-01: 78_750 - 2020-01-01: 80_000 - 2021-01-01: 80_800 - 2022-01-01: 83_350 - 2023-01-01: 89_250 - 2024-01-01: 94_050 - SEPARATE: - values: - 2013-01-01: 36_250 - 2014-01-01: 36_900 - 2015-01-01: 37_450 - 2016-01-01: 37_650 - 2017-01-01: 37_950 - 2018-01-01: 38_600 - 2019-01-01: 39_375 - 2020-01-01: 40_000 - 2021-01-01: 40_400 - 2022-01-01: 41_675 - 2023-01-01: 44_625 - 2024-01-01: 47_025 - SINGLE: - values: - 2013-01-01: 36_250 - 2014-01-01: 36_900 - 2015-01-01: 37_450 - 2016-01-01: 37_650 - 2017-01-01: 37_950 - 2018-01-01: 38_600 - 2019-01-01: 39_375 - 2020-01-01: 40_000 - 2021-01-01: 40_400 - 2022-01-01: 41_675 - 2023-01-01: 44_625 - 2024-01-01: 47_025 - SURVIVING_SPOUSE: - values: - 2013-01-01: 72_500 - 2014-01-01: 73_800 - 2015-01-01: 74_900 - 2016-01-01: 75_300 - 2017-01-01: 75_900 - 2018-01-01: 77_200 - 2019-01-01: 78_750 - 2020-01-01: 80_000 - 2021-01-01: 80_800 - 2022-01-01: 83_350 - 2023-01-01: 89_250 - 2024-01-01: 94_050 - 2: - HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 425_000 - 2014-01-01: 432_200 - 2015-01-01: 439_000 - 2016-01-01: 441_000 - 2017-01-01: 444_550 - 2018-01-01: 452_400 - 2019-01-01: 461_700 - 2020-01-01: 469_050 - 2021-01-01: 473_750 - 2022-01-01: 488_500 - 2023-01-01: 523_050 - 2024-01-01: 551_350 - JOINT: - values: - 2013-01-01: 450_000 - 2014-01-01: 457_600 - 2015-01-01: 464_850 - 2016-01-01: 466_950 - 2017-01-01: 470_700 - 2018-01-01: 479_000 - 2019-01-01: 488_850 - 2020-01-01: 496_600 - 2021-01-01: 501_600 - 2022-01-01: 517_200 - 2023-01-01: 553_850 - 2024-01-01: 583_750 - SEPARATE: - values: - 2013-01-01: 225_000 - 2014-01-01: 228_800 - 2015-01-01: 232_425 - 2016-01-01: 233_475 - 2017-01-01: 235_350 - 2018-01-01: 239_500 - 2019-01-01: 244_425 - 2020-01-01: 248_300 - 2021-01-01: 250_800 - 2022-01-01: 258_600 - 2023-01-01: 276_900 - 2024-01-01: 291_850 - SINGLE: - values: - 2013-01-01: 400_000 - 2014-01-01: 406_750 - 2015-01-01: 413_200 - 2016-01-01: 415_050 - 2017-01-01: 418_400 - 2018-01-01: 425_800 - 2019-01-01: 434_550 - 2020-01-01: 441_450 - 2021-01-01: 445_850 - 2022-01-01: 459_750 - 2023-01-01: 492_300 - 2024-01-01: 518_900 - SURVIVING_SPOUSE: - values: - 2013-01-01: 450_000 - 2014-01-01: 457_600 - 2015-01-01: 464_850 - 2016-01-01: 466_950 - 2017-01-01: 470_700 - 2018-01-01: 479_000 - 2019-01-01: 488_850 - 2020-01-01: 496_600 - 2021-01-01: 501_600 - 2022-01-01: 517_200 - 2023-01-01: 553_850 - 2024-01-01: 583_750 - - 3: - HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 1_000_000 - JOINT: - values: - 2013-01-01: 1_000_000 - SEPARATE: - values: - 2013-01-01: 1_000_000 - SINGLE: - values: - 2013-01-01: 1_000_000 - SURVIVING_SPOUSE: - values: - 2013-01-01: 1_000_000 diff --git a/policyengine_us/parameters/gov/contrib/harris/capital_gains/rates.yaml b/policyengine_us/parameters/gov/contrib/harris/capital_gains/rates.yaml new file mode 100644 index 00000000000..fd1da658db3 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/harris/capital_gains/rates.yaml @@ -0,0 +1,12 @@ +description: Harris proposed to tax capital gains at the following rates. +1: + 2013-01-01: 0 +2: + 2013-01-01: 0.15 +3: + 2013-01-01: 0.2 +4: + 2013-01-01: 0.28 +metadata: + unit: /1 + label: Harris capital gains tax rates diff --git a/policyengine_us/parameters/gov/contrib/harris/capital_gains/thresholds.yaml b/policyengine_us/parameters/gov/contrib/harris/capital_gains/thresholds.yaml new file mode 100644 index 00000000000..2edde34f352 --- /dev/null +++ b/policyengine_us/parameters/gov/contrib/harris/capital_gains/thresholds.yaml @@ -0,0 +1,183 @@ +description: Top thresholds of Harris proposed capital gains tax brackets +metadata: + unit: currency-USD + period: year + label: Harris capital gains tax rate thresholds + breakdown: + - range(1, 4) + - filing_status + propagate_metadata_to_children: true + reference: + - title: Internal Revenue Code ยง1(j)(5)(B) + href: https://www.law.cornell.edu/uscode/text/26/1#j_5_B + # IRC defines amounts as of 2018. Defines COLA with rounded up to the nearest multiple of $50. + - title: 2024 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-23-34.pdf#page=8 + - title: 2023 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-22-38.pdf#page=8 + - title: 2022 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-21-45.pdf#page=8 + - title: 2021 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-20-45.pdf#page=8 + - title: 2020 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-19-44.pdf#page=8 + - title: 2019 IRS data release + href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=8 +1: + HEAD_OF_HOUSEHOLD: + values: + 2013-01-01: 48_600 + 2014-01-01: 49_400 + 2015-01-01: 50_200 + 2016-01-01: 50_400 + 2017-01-01: 50_800 + 2018-01-01: 51_700 + 2019-01-01: 52_750 + 2020-01-01: 53_600 + 2021-01-01: 54_100 + 2022-01-01: 55_800 + 2023-01-01: 59_750 + 2024-01-01: 63_000 + JOINT: + values: + 2013-01-01: 72_500 + 2014-01-01: 73_800 + 2015-01-01: 74_900 + 2016-01-01: 75_300 + 2017-01-01: 75_900 + 2018-01-01: 77_200 + 2019-01-01: 78_750 + 2020-01-01: 80_000 + 2021-01-01: 80_800 + 2022-01-01: 83_350 + 2023-01-01: 89_250 + 2024-01-01: 94_050 + SEPARATE: + values: + 2013-01-01: 36_250 + 2014-01-01: 36_900 + 2015-01-01: 37_450 + 2016-01-01: 37_650 + 2017-01-01: 37_950 + 2018-01-01: 38_600 + 2019-01-01: 39_375 + 2020-01-01: 40_000 + 2021-01-01: 40_400 + 2022-01-01: 41_675 + 2023-01-01: 44_625 + 2024-01-01: 47_025 + SINGLE: + values: + 2013-01-01: 36_250 + 2014-01-01: 36_900 + 2015-01-01: 37_450 + 2016-01-01: 37_650 + 2017-01-01: 37_950 + 2018-01-01: 38_600 + 2019-01-01: 39_375 + 2020-01-01: 40_000 + 2021-01-01: 40_400 + 2022-01-01: 41_675 + 2023-01-01: 44_625 + 2024-01-01: 47_025 + SURVIVING_SPOUSE: + values: + 2013-01-01: 72_500 + 2014-01-01: 73_800 + 2015-01-01: 74_900 + 2016-01-01: 75_300 + 2017-01-01: 75_900 + 2018-01-01: 77_200 + 2019-01-01: 78_750 + 2020-01-01: 80_000 + 2021-01-01: 80_800 + 2022-01-01: 83_350 + 2023-01-01: 89_250 + 2024-01-01: 94_050 +2: + HEAD_OF_HOUSEHOLD: + values: + 2013-01-01: 425_000 + 2014-01-01: 432_200 + 2015-01-01: 439_000 + 2016-01-01: 441_000 + 2017-01-01: 444_550 + 2018-01-01: 452_400 + 2019-01-01: 461_700 + 2020-01-01: 469_050 + 2021-01-01: 473_750 + 2022-01-01: 488_500 + 2023-01-01: 523_050 + 2024-01-01: 551_350 + JOINT: + values: + 2013-01-01: 450_000 + 2014-01-01: 457_600 + 2015-01-01: 464_850 + 2016-01-01: 466_950 + 2017-01-01: 470_700 + 2018-01-01: 479_000 + 2019-01-01: 488_850 + 2020-01-01: 496_600 + 2021-01-01: 501_600 + 2022-01-01: 517_200 + 2023-01-01: 553_850 + 2024-01-01: 583_750 + SEPARATE: + values: + 2013-01-01: 225_000 + 2014-01-01: 228_800 + 2015-01-01: 232_425 + 2016-01-01: 233_475 + 2017-01-01: 235_350 + 2018-01-01: 239_500 + 2019-01-01: 244_425 + 2020-01-01: 248_300 + 2021-01-01: 250_800 + 2022-01-01: 258_600 + 2023-01-01: 276_900 + 2024-01-01: 291_850 + SINGLE: + values: + 2013-01-01: 400_000 + 2014-01-01: 406_750 + 2015-01-01: 413_200 + 2016-01-01: 415_050 + 2017-01-01: 418_400 + 2018-01-01: 425_800 + 2019-01-01: 434_550 + 2020-01-01: 441_450 + 2021-01-01: 445_850 + 2022-01-01: 459_750 + 2023-01-01: 492_300 + 2024-01-01: 518_900 + SURVIVING_SPOUSE: + values: + 2013-01-01: 450_000 + 2014-01-01: 457_600 + 2015-01-01: 464_850 + 2016-01-01: 466_950 + 2017-01-01: 470_700 + 2018-01-01: 479_000 + 2019-01-01: 488_850 + 2020-01-01: 496_600 + 2021-01-01: 501_600 + 2022-01-01: 517_200 + 2023-01-01: 553_850 + 2024-01-01: 583_750 +3: + HEAD_OF_HOUSEHOLD: + values: + 2013-01-01: 1_000_000 + JOINT: + values: + 2013-01-01: 1_000_000 + SEPARATE: + values: + 2013-01-01: 1_000_000 + SINGLE: + values: + 2013-01-01: 1_000_000 + SURVIVING_SPOUSE: + values: + 2013-01-01: 1_000_000 diff --git a/policyengine_us/reforms/harris/capital_gains/harris_capital_gains.py b/policyengine_us/reforms/harris/capital_gains/harris_capital_gains.py index f97815a599d..6abfd2bf1c4 100644 --- a/policyengine_us/reforms/harris/capital_gains/harris_capital_gains.py +++ b/policyengine_us/reforms/harris/capital_gains/harris_capital_gains.py @@ -29,9 +29,9 @@ def formula(tax_unit, period, parameters): filing_status = tax_unit("filing_status", period) - first_threshold = p_ref.brackets.thresholds["1"][filing_status] - second_threshold = p_ref.brackets.thresholds["2"][filing_status] - third_threshold = p_ref.brackets.thresholds["3"][filing_status] + first_threshold = p_ref.thresholds["1"][filing_status] + second_threshold = p_ref.thresholds["2"][filing_status] + third_threshold = p_ref.thresholds["3"][filing_status] income_ordinarily_under_second_rate = clip( taxable_income, 0, first_threshold @@ -82,10 +82,10 @@ def formula(tax_unit, period, parameters): ) main_cg_tax = ( - cg_in_first_bracket * p_ref.brackets.rates["1"] - + cg_in_second_bracket * p_ref.brackets.rates["2"] - + cg_in_third_bracket * p_ref.brackets.rates["3"] - + cg_in_fourth_bracket * p_ref.brackets.rates["4"] + cg_in_first_bracket * p_ref.rates["1"] + + cg_in_second_bracket * p_ref.rates["2"] + + cg_in_third_bracket * p_ref.rates["3"] + + cg_in_fourth_bracket * p_ref.rates["4"] ) unrecaptured_s_1250_gain = tax_unit( From 6bb30e68997ff0c01fc7484774bbf46af39c6e27 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 19 Oct 2025 10:42:43 -0400 Subject: [PATCH 45/53] Apply Black formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../reforms/biden/budget_2025/capital_gains_tax_increase.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/policyengine_us/reforms/biden/budget_2025/capital_gains_tax_increase.py b/policyengine_us/reforms/biden/budget_2025/capital_gains_tax_increase.py index bbb87821bfc..c3a942ddc9e 100644 --- a/policyengine_us/reforms/biden/budget_2025/capital_gains_tax_increase.py +++ b/policyengine_us/reforms/biden/budget_2025/capital_gains_tax_increase.py @@ -93,10 +93,8 @@ def formula(tax_unit, period, parameters): main_cg_tax = ( cg_in_first_bracket_below_top_bracket * cg.rates["1"] - + cg_in_second_bracket_below_top_bracket - * cg.rates["2"] - + cg_in_third_bracket_below_top_bracket - * cg.rates["3"] + + cg_in_second_bracket_below_top_bracket * cg.rates["2"] + + cg_in_third_bracket_below_top_bracket * cg.rates["3"] + new_cg_tax ) From c443418c854893680a0cdb8cdd2203a9d754548a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 19 Oct 2025 10:48:36 -0400 Subject: [PATCH 46/53] Remove unnecessary values wrappers from capital gains thresholds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify YAML structure by removing 'values:' wrappers from filing status breakdowns. Metadata propagation handles this without explicit wrappers. Addresses Pavel's review feedback. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../harris/capital_gains/thresholds.yaml | 265 ++++++++-------- .../gov/irs/capital_gains/thresholds.yaml | 290 +++++++++--------- 2 files changed, 265 insertions(+), 290 deletions(-) diff --git a/policyengine_us/parameters/gov/contrib/harris/capital_gains/thresholds.yaml b/policyengine_us/parameters/gov/contrib/harris/capital_gains/thresholds.yaml index 2edde34f352..b16eed5e8ad 100644 --- a/policyengine_us/parameters/gov/contrib/harris/capital_gains/thresholds.yaml +++ b/policyengine_us/parameters/gov/contrib/harris/capital_gains/thresholds.yaml @@ -25,159 +25,144 @@ metadata: href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=8 1: HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 48_600 - 2014-01-01: 49_400 - 2015-01-01: 50_200 - 2016-01-01: 50_400 - 2017-01-01: 50_800 - 2018-01-01: 51_700 - 2019-01-01: 52_750 - 2020-01-01: 53_600 - 2021-01-01: 54_100 - 2022-01-01: 55_800 - 2023-01-01: 59_750 - 2024-01-01: 63_000 + 2013-01-01: 48_600 + 2014-01-01: 49_400 + 2015-01-01: 50_200 + 2016-01-01: 50_400 + 2017-01-01: 50_800 + 2018-01-01: 51_700 + 2019-01-01: 52_750 + 2020-01-01: 53_600 + 2021-01-01: 54_100 + 2022-01-01: 55_800 + 2023-01-01: 59_750 + 2024-01-01: 63_000 JOINT: - values: - 2013-01-01: 72_500 - 2014-01-01: 73_800 - 2015-01-01: 74_900 - 2016-01-01: 75_300 - 2017-01-01: 75_900 - 2018-01-01: 77_200 - 2019-01-01: 78_750 - 2020-01-01: 80_000 - 2021-01-01: 80_800 - 2022-01-01: 83_350 - 2023-01-01: 89_250 - 2024-01-01: 94_050 + 2013-01-01: 72_500 + 2014-01-01: 73_800 + 2015-01-01: 74_900 + 2016-01-01: 75_300 + 2017-01-01: 75_900 + 2018-01-01: 77_200 + 2019-01-01: 78_750 + 2020-01-01: 80_000 + 2021-01-01: 80_800 + 2022-01-01: 83_350 + 2023-01-01: 89_250 + 2024-01-01: 94_050 SEPARATE: - values: - 2013-01-01: 36_250 - 2014-01-01: 36_900 - 2015-01-01: 37_450 - 2016-01-01: 37_650 - 2017-01-01: 37_950 - 2018-01-01: 38_600 - 2019-01-01: 39_375 - 2020-01-01: 40_000 - 2021-01-01: 40_400 - 2022-01-01: 41_675 - 2023-01-01: 44_625 - 2024-01-01: 47_025 + 2013-01-01: 36_250 + 2014-01-01: 36_900 + 2015-01-01: 37_450 + 2016-01-01: 37_650 + 2017-01-01: 37_950 + 2018-01-01: 38_600 + 2019-01-01: 39_375 + 2020-01-01: 40_000 + 2021-01-01: 40_400 + 2022-01-01: 41_675 + 2023-01-01: 44_625 + 2024-01-01: 47_025 SINGLE: - values: - 2013-01-01: 36_250 - 2014-01-01: 36_900 - 2015-01-01: 37_450 - 2016-01-01: 37_650 - 2017-01-01: 37_950 - 2018-01-01: 38_600 - 2019-01-01: 39_375 - 2020-01-01: 40_000 - 2021-01-01: 40_400 - 2022-01-01: 41_675 - 2023-01-01: 44_625 - 2024-01-01: 47_025 + 2013-01-01: 36_250 + 2014-01-01: 36_900 + 2015-01-01: 37_450 + 2016-01-01: 37_650 + 2017-01-01: 37_950 + 2018-01-01: 38_600 + 2019-01-01: 39_375 + 2020-01-01: 40_000 + 2021-01-01: 40_400 + 2022-01-01: 41_675 + 2023-01-01: 44_625 + 2024-01-01: 47_025 SURVIVING_SPOUSE: - values: - 2013-01-01: 72_500 - 2014-01-01: 73_800 - 2015-01-01: 74_900 - 2016-01-01: 75_300 - 2017-01-01: 75_900 - 2018-01-01: 77_200 - 2019-01-01: 78_750 - 2020-01-01: 80_000 - 2021-01-01: 80_800 - 2022-01-01: 83_350 - 2023-01-01: 89_250 - 2024-01-01: 94_050 + 2013-01-01: 72_500 + 2014-01-01: 73_800 + 2015-01-01: 74_900 + 2016-01-01: 75_300 + 2017-01-01: 75_900 + 2018-01-01: 77_200 + 2019-01-01: 78_750 + 2020-01-01: 80_000 + 2021-01-01: 80_800 + 2022-01-01: 83_350 + 2023-01-01: 89_250 + 2024-01-01: 94_050 2: HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 425_000 - 2014-01-01: 432_200 - 2015-01-01: 439_000 - 2016-01-01: 441_000 - 2017-01-01: 444_550 - 2018-01-01: 452_400 - 2019-01-01: 461_700 - 2020-01-01: 469_050 - 2021-01-01: 473_750 - 2022-01-01: 488_500 - 2023-01-01: 523_050 - 2024-01-01: 551_350 + 2013-01-01: 425_000 + 2014-01-01: 432_200 + 2015-01-01: 439_000 + 2016-01-01: 441_000 + 2017-01-01: 444_550 + 2018-01-01: 452_400 + 2019-01-01: 461_700 + 2020-01-01: 469_050 + 2021-01-01: 473_750 + 2022-01-01: 488_500 + 2023-01-01: 523_050 + 2024-01-01: 551_350 JOINT: - values: - 2013-01-01: 450_000 - 2014-01-01: 457_600 - 2015-01-01: 464_850 - 2016-01-01: 466_950 - 2017-01-01: 470_700 - 2018-01-01: 479_000 - 2019-01-01: 488_850 - 2020-01-01: 496_600 - 2021-01-01: 501_600 - 2022-01-01: 517_200 - 2023-01-01: 553_850 - 2024-01-01: 583_750 + 2013-01-01: 450_000 + 2014-01-01: 457_600 + 2015-01-01: 464_850 + 2016-01-01: 466_950 + 2017-01-01: 470_700 + 2018-01-01: 479_000 + 2019-01-01: 488_850 + 2020-01-01: 496_600 + 2021-01-01: 501_600 + 2022-01-01: 517_200 + 2023-01-01: 553_850 + 2024-01-01: 583_750 SEPARATE: - values: - 2013-01-01: 225_000 - 2014-01-01: 228_800 - 2015-01-01: 232_425 - 2016-01-01: 233_475 - 2017-01-01: 235_350 - 2018-01-01: 239_500 - 2019-01-01: 244_425 - 2020-01-01: 248_300 - 2021-01-01: 250_800 - 2022-01-01: 258_600 - 2023-01-01: 276_900 - 2024-01-01: 291_850 + 2013-01-01: 225_000 + 2014-01-01: 228_800 + 2015-01-01: 232_425 + 2016-01-01: 233_475 + 2017-01-01: 235_350 + 2018-01-01: 239_500 + 2019-01-01: 244_425 + 2020-01-01: 248_300 + 2021-01-01: 250_800 + 2022-01-01: 258_600 + 2023-01-01: 276_900 + 2024-01-01: 291_850 SINGLE: - values: - 2013-01-01: 400_000 - 2014-01-01: 406_750 - 2015-01-01: 413_200 - 2016-01-01: 415_050 - 2017-01-01: 418_400 - 2018-01-01: 425_800 - 2019-01-01: 434_550 - 2020-01-01: 441_450 - 2021-01-01: 445_850 - 2022-01-01: 459_750 - 2023-01-01: 492_300 - 2024-01-01: 518_900 + 2013-01-01: 400_000 + 2014-01-01: 406_750 + 2015-01-01: 413_200 + 2016-01-01: 415_050 + 2017-01-01: 418_400 + 2018-01-01: 425_800 + 2019-01-01: 434_550 + 2020-01-01: 441_450 + 2021-01-01: 445_850 + 2022-01-01: 459_750 + 2023-01-01: 492_300 + 2024-01-01: 518_900 SURVIVING_SPOUSE: - values: - 2013-01-01: 450_000 - 2014-01-01: 457_600 - 2015-01-01: 464_850 - 2016-01-01: 466_950 - 2017-01-01: 470_700 - 2018-01-01: 479_000 - 2019-01-01: 488_850 - 2020-01-01: 496_600 - 2021-01-01: 501_600 - 2022-01-01: 517_200 - 2023-01-01: 553_850 - 2024-01-01: 583_750 + 2013-01-01: 450_000 + 2014-01-01: 457_600 + 2015-01-01: 464_850 + 2016-01-01: 466_950 + 2017-01-01: 470_700 + 2018-01-01: 479_000 + 2019-01-01: 488_850 + 2020-01-01: 496_600 + 2021-01-01: 501_600 + 2022-01-01: 517_200 + 2023-01-01: 553_850 + 2024-01-01: 583_750 3: HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 1_000_000 + 2013-01-01: 1_000_000 JOINT: - values: - 2013-01-01: 1_000_000 + 2013-01-01: 1_000_000 SEPARATE: - values: - 2013-01-01: 1_000_000 + 2013-01-01: 1_000_000 SINGLE: - values: - 2013-01-01: 1_000_000 + 2013-01-01: 1_000_000 SURVIVING_SPOUSE: - values: - 2013-01-01: 1_000_000 + 2013-01-01: 1_000_000 diff --git a/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml index 2a757287caa..44d4ae1d106 100644 --- a/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml +++ b/policyengine_us/parameters/gov/irs/capital_gains/thresholds.yaml @@ -30,163 +30,153 @@ metadata: href: https://www.irs.gov/pub/irs-drop/rp-18-57.pdf#page=8 1: HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 48_600 - 2014-01-01: 49_400 - 2015-01-01: 50_200 - 2016-01-01: 50_400 - 2017-01-01: 50_800 - 2018-01-01: 51_700 - 2019-01-01: 52_750 - 2020-01-01: 53_600 - 2021-01-01: 54_100 - 2022-01-01: 55_800 - 2023-01-01: 59_750 - 2024-01-01: 63_000 - 2025-01-01: 64_750 - 2026-01-01: 66_200 + 2013-01-01: 48_600 + 2014-01-01: 49_400 + 2015-01-01: 50_200 + 2016-01-01: 50_400 + 2017-01-01: 50_800 + 2018-01-01: 51_700 + 2019-01-01: 52_750 + 2020-01-01: 53_600 + 2021-01-01: 54_100 + 2022-01-01: 55_800 + 2023-01-01: 59_750 + 2024-01-01: 63_000 + 2025-01-01: 64_750 + 2026-01-01: 66_200 JOINT: - values: - 2013-01-01: 72_500 - 2014-01-01: 73_800 - 2015-01-01: 74_900 - 2016-01-01: 75_300 - 2017-01-01: 75_900 - 2018-01-01: 77_200 - 2019-01-01: 78_750 - 2020-01-01: 80_000 - 2021-01-01: 80_800 - 2022-01-01: 83_350 - 2023-01-01: 89_250 - 2024-01-01: 94_050 - 2025-01-01: 96_700 - 2026-01-01: 98_900 + 2013-01-01: 72_500 + 2014-01-01: 73_800 + 2015-01-01: 74_900 + 2016-01-01: 75_300 + 2017-01-01: 75_900 + 2018-01-01: 77_200 + 2019-01-01: 78_750 + 2020-01-01: 80_000 + 2021-01-01: 80_800 + 2022-01-01: 83_350 + 2023-01-01: 89_250 + 2024-01-01: 94_050 + 2025-01-01: 96_700 + 2026-01-01: 98_900 SEPARATE: - values: - 2013-01-01: 36_250 - 2014-01-01: 36_900 - 2015-01-01: 37_450 - 2016-01-01: 37_650 - 2017-01-01: 37_950 - 2018-01-01: 38_600 - 2019-01-01: 39_375 - 2020-01-01: 40_000 - 2021-01-01: 40_400 - 2022-01-01: 41_675 - 2023-01-01: 44_625 - 2024-01-01: 47_025 - 2025-01-01: 48_350 - 2026-01-01: 49_450 + 2013-01-01: 36_250 + 2014-01-01: 36_900 + 2015-01-01: 37_450 + 2016-01-01: 37_650 + 2017-01-01: 37_950 + 2018-01-01: 38_600 + 2019-01-01: 39_375 + 2020-01-01: 40_000 + 2021-01-01: 40_400 + 2022-01-01: 41_675 + 2023-01-01: 44_625 + 2024-01-01: 47_025 + 2025-01-01: 48_350 + 2026-01-01: 49_450 SINGLE: - values: - 2013-01-01: 36_250 - 2014-01-01: 36_900 - 2015-01-01: 37_450 - 2016-01-01: 37_650 - 2017-01-01: 37_950 - 2018-01-01: 38_600 - 2019-01-01: 39_375 - 2020-01-01: 40_000 - 2021-01-01: 40_400 - 2022-01-01: 41_675 - 2023-01-01: 44_625 - 2024-01-01: 47_025 - 2025-01-01: 48_350 - 2026-01-01: 49_450 + 2013-01-01: 36_250 + 2014-01-01: 36_900 + 2015-01-01: 37_450 + 2016-01-01: 37_650 + 2017-01-01: 37_950 + 2018-01-01: 38_600 + 2019-01-01: 39_375 + 2020-01-01: 40_000 + 2021-01-01: 40_400 + 2022-01-01: 41_675 + 2023-01-01: 44_625 + 2024-01-01: 47_025 + 2025-01-01: 48_350 + 2026-01-01: 49_450 SURVIVING_SPOUSE: - values: - 2013-01-01: 72_500 - 2014-01-01: 73_800 - 2015-01-01: 74_900 - 2016-01-01: 75_300 - 2017-01-01: 75_900 - 2018-01-01: 77_200 - 2019-01-01: 78_750 - 2020-01-01: 80_000 - 2021-01-01: 80_800 - 2022-01-01: 83_350 - 2023-01-01: 89_250 - 2024-01-01: 94_050 - 2025-01-01: 96_700 - 2026-01-01: 98_900 + 2013-01-01: 72_500 + 2014-01-01: 73_800 + 2015-01-01: 74_900 + 2016-01-01: 75_300 + 2017-01-01: 75_900 + 2018-01-01: 77_200 + 2019-01-01: 78_750 + 2020-01-01: 80_000 + 2021-01-01: 80_800 + 2022-01-01: 83_350 + 2023-01-01: 89_250 + 2024-01-01: 94_050 + 2025-01-01: 96_700 + 2026-01-01: 98_900 2: HEAD_OF_HOUSEHOLD: - values: - 2013-01-01: 425_000 - 2014-01-01: 432_200 - 2015-01-01: 439_000 - 2016-01-01: 441_000 - 2017-01-01: 444_550 - 2018-01-01: 452_400 - 2019-01-01: 461_700 - 2020-01-01: 469_050 - 2021-01-01: 473_750 - 2022-01-01: 488_500 - 2023-01-01: 523_050 - 2024-01-01: 551_350 - 2025-01-01: 566_700 - 2026-01-01: 579_600 + 2013-01-01: 425_000 + 2014-01-01: 432_200 + 2015-01-01: 439_000 + 2016-01-01: 441_000 + 2017-01-01: 444_550 + 2018-01-01: 452_400 + 2019-01-01: 461_700 + 2020-01-01: 469_050 + 2021-01-01: 473_750 + 2022-01-01: 488_500 + 2023-01-01: 523_050 + 2024-01-01: 551_350 + 2025-01-01: 566_700 + 2026-01-01: 579_600 JOINT: - values: - 2013-01-01: 450_000 - 2014-01-01: 457_600 - 2015-01-01: 464_850 - 2016-01-01: 466_950 - 2017-01-01: 470_700 - 2018-01-01: 479_000 - 2019-01-01: 488_850 - 2020-01-01: 496_600 - 2021-01-01: 501_600 - 2022-01-01: 517_200 - 2023-01-01: 553_850 - 2024-01-01: 583_750 - 2025-01-01: 600_050 - 2026-01-01: 613_700 + 2013-01-01: 450_000 + 2014-01-01: 457_600 + 2015-01-01: 464_850 + 2016-01-01: 466_950 + 2017-01-01: 470_700 + 2018-01-01: 479_000 + 2019-01-01: 488_850 + 2020-01-01: 496_600 + 2021-01-01: 501_600 + 2022-01-01: 517_200 + 2023-01-01: 553_850 + 2024-01-01: 583_750 + 2025-01-01: 600_050 + 2026-01-01: 613_700 SEPARATE: - values: - 2013-01-01: 225_000 - 2014-01-01: 228_800 - 2015-01-01: 232_425 - 2016-01-01: 233_475 - 2017-01-01: 235_350 - 2018-01-01: 239_500 - 2019-01-01: 244_425 - 2020-01-01: 248_300 - 2021-01-01: 250_800 - 2022-01-01: 258_600 - 2023-01-01: 276_900 - 2024-01-01: 291_850 - 2025-01-01: 300_000 - 2026-01-01: 306_850 + 2013-01-01: 225_000 + 2014-01-01: 228_800 + 2015-01-01: 232_425 + 2016-01-01: 233_475 + 2017-01-01: 235_350 + 2018-01-01: 239_500 + 2019-01-01: 244_425 + 2020-01-01: 248_300 + 2021-01-01: 250_800 + 2022-01-01: 258_600 + 2023-01-01: 276_900 + 2024-01-01: 291_850 + 2025-01-01: 300_000 + 2026-01-01: 306_850 SINGLE: - values: - 2013-01-01: 400_000 - 2014-01-01: 406_750 - 2015-01-01: 413_200 - 2016-01-01: 415_050 - 2017-01-01: 418_400 - 2018-01-01: 425_800 - 2019-01-01: 434_550 - 2020-01-01: 441_450 - 2021-01-01: 445_850 - 2022-01-01: 459_750 - 2023-01-01: 492_300 - 2024-01-01: 518_900 - 2025-01-01: 533_400 - 2026-01-01: 545_500 + 2013-01-01: 400_000 + 2014-01-01: 406_750 + 2015-01-01: 413_200 + 2016-01-01: 415_050 + 2017-01-01: 418_400 + 2018-01-01: 425_800 + 2019-01-01: 434_550 + 2020-01-01: 441_450 + 2021-01-01: 445_850 + 2022-01-01: 459_750 + 2023-01-01: 492_300 + 2024-01-01: 518_900 + 2025-01-01: 533_400 + 2026-01-01: 545_500 SURVIVING_SPOUSE: - values: - 2013-01-01: 450_000 - 2014-01-01: 457_600 - 2015-01-01: 464_850 - 2016-01-01: 466_950 - 2017-01-01: 470_700 - 2018-01-01: 479_000 - 2019-01-01: 488_850 - 2020-01-01: 496_600 - 2021-01-01: 501_600 - 2022-01-01: 517_200 - 2023-01-01: 553_850 - 2024-01-01: 583_750 - 2025-01-01: 600_050 - 2026-01-01: 613_700 + 2013-01-01: 450_000 + 2014-01-01: 457_600 + 2015-01-01: 464_850 + 2016-01-01: 466_950 + 2017-01-01: 470_700 + 2018-01-01: 479_000 + 2019-01-01: 488_850 + 2020-01-01: 496_600 + 2021-01-01: 501_600 + 2022-01-01: 517_200 + 2023-01-01: 553_850 + 2024-01-01: 583_750 + 2025-01-01: 600_050 + 2026-01-01: 613_700 From 07efb0e9f8fe0fe6a0a9caf08c61010556beb36a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 19 Oct 2025 14:05:24 -0400 Subject: [PATCH 47/53] Update test expectations for corrected tax bracket rounding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update 2027 tax expectations to reflect downwards rounding (per IRC ยง 1(f)(7)(A)) instead of nearest rounding. Changes due to fix in tax bracket threshold uprating: - Test 'Additional tax bracket is not applied': 290_683 โ†’ 290_689 - Test 'Additional tax bracket is not applied, taxable income is below threshold': 146_350 โ†’ 146_355 ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../additional_tax_bracket_reform.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/policyengine_us/tests/policy/contrib/additional_tax_bracket/additional_tax_bracket_reform.yaml b/policyengine_us/tests/policy/contrib/additional_tax_bracket/additional_tax_bracket_reform.yaml index d869ae7d38c..f541625a9d4 100644 --- a/policyengine_us/tests/policy/contrib/additional_tax_bracket/additional_tax_bracket_reform.yaml +++ b/policyengine_us/tests/policy/contrib/additional_tax_bracket/additional_tax_bracket_reform.yaml @@ -22,8 +22,8 @@ taxable_income: 1_000_000 filing_status: JOINT output: - regular_tax_before_credits: 290_683 - income_tax_main_rates: 290_683 + regular_tax_before_credits: 290_689 + income_tax_main_rates: 290_689 - name: Additional tax bracket is applied, taxable income is below the threshold. period: 2027 @@ -49,5 +49,5 @@ taxable_income: 600_000 filing_status: JOINT output: - regular_tax_before_credits: 146_350 - income_tax_main_rates: 146_350 + regular_tax_before_credits: 146_355 + income_tax_main_rates: 146_355 From f565013ff4209d5f82e9b02f10d30e2703aa63fa Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 20 Oct 2025 06:40:14 -0400 Subject: [PATCH 48/53] Add 2024-2025 retirement catch-up contribution values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add IRS-published catch-up values for consistency with other parameters: **IRA catch-up:** - 2024: $1,000 (SECURE 2.0 indexing started but no increase due to inflation) - 2025: $1,000 (remains unchanged) **401(k) catch-up:** - 2024: $7,500 - 2025: $7,500 These values establish proper uprating base points from recent IRS data rather than uprating from 2018/2023. Note: 401(k) enhanced catch-up for ages 60-63 ($11,250 in 2025) tracked in issue #6712. Addresses Pavel's review comment about uprating base periods. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../retirement_contributions/catch_up/limit/401k.yaml | 2 ++ .../retirement_contributions/catch_up/limit/ira.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml index 818328099b4..b03a34e33d1 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/401k.yaml @@ -3,6 +3,8 @@ values: 2018-01-01: 6_000 2020-01-01: 6_500 2023-01-01: 7_500 + 2024-01-01: 7_500 + 2025-01-01: 7_500 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml index 2d81aae7717..034263f142f 100644 --- a/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml +++ b/policyengine_us/parameters/gov/irs/gross_income/retirement_contributions/catch_up/limit/ira.yaml @@ -1,6 +1,8 @@ description: The US allows for catch-up IRA contributions of this amount. values: 2018-01-01: 1_000 + 2024-01-01: 1_000 + 2025-01-01: 1_000 metadata: unit: currency-USD period: year From 2843ec051707528b31cf565c5f61ebee68c8877e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 29 Oct 2025 22:41:14 -0400 Subject: [PATCH 49/53] Add TOB revenue variable and fix LSR recursion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements trust fund revenue calculation from SS benefit taxation using branching + neutralization (the correct approach). Changes: 1. New variable: tob_revenue_total - calculates trust fund revenue 2. Fix LSR recursion guard to prevent infinite loops 3. Test files demonstrating the approach Results: - Baseline TOB revenue (2026): $85.33B - Option 2 TOB revenue (2026): $109.62B LSR recursion fix adds re-entry guard to prevent loops when branches calculate income_tax. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../gov/ssa/revenue/tob_revenue_total.yaml | 19 ++++ .../labor_supply_behavioral_response.py | 96 +++++++++++-------- .../gov/ssa/revenue/tob_revenue_total.py | 42 ++++++++ test_branch_calc_respects_neutralization.py | 46 +++++++++ test_branch_neutralization_inheritance.py | 49 ++++++++++ test_clone_independence.py | 32 +++++++ test_lsr_branch_creation.py | 56 +++++++++++ test_lsr_reentry_guard.py | 45 +++++++++ test_lsr_simple.py | 34 +++++++ test_neutralization_in_branch.py | 30 ++++++ test_tob_onmodel.py | 21 ++++ test_tob_option2.py | 31 ++++++ test_tob_with_lsr.py | 79 +++++++++++++++ uv.lock | 2 +- 14 files changed, 539 insertions(+), 43 deletions(-) create mode 100644 policyengine_us/tests/policy/baseline/gov/ssa/revenue/tob_revenue_total.yaml create mode 100644 policyengine_us/variables/gov/ssa/revenue/tob_revenue_total.py create mode 100644 test_branch_calc_respects_neutralization.py create mode 100644 test_branch_neutralization_inheritance.py create mode 100644 test_clone_independence.py create mode 100644 test_lsr_branch_creation.py create mode 100644 test_lsr_reentry_guard.py create mode 100644 test_lsr_simple.py create mode 100644 test_neutralization_in_branch.py create mode 100644 test_tob_onmodel.py create mode 100644 test_tob_option2.py create mode 100644 test_tob_with_lsr.py diff --git a/policyengine_us/tests/policy/baseline/gov/ssa/revenue/tob_revenue_total.yaml b/policyengine_us/tests/policy/baseline/gov/ssa/revenue/tob_revenue_total.yaml new file mode 100644 index 00000000000..165e9fbd15e --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/ssa/revenue/tob_revenue_total.yaml @@ -0,0 +1,19 @@ +- name: TOB revenue - single retiree with SS and wages + period: 2024 + absolute_error_margin: 100 + input: + people: + person1: + age: 67 + social_security: 30_000 + employment_income: 50_000 + tax_units: + tax_unit: + members: [person1] + filing_status: SINGLE + households: + household: + members: [person1] + output: + # Should be positive and substantial + tob_revenue_total: [800, 3_000] diff --git a/policyengine_us/variables/gov/simulation/labor_supply_response/labor_supply_behavioral_response.py b/policyengine_us/variables/gov/simulation/labor_supply_response/labor_supply_behavioral_response.py index c606245e049..c65224feb7a 100644 --- a/policyengine_us/variables/gov/simulation/labor_supply_response/labor_supply_behavioral_response.py +++ b/policyengine_us/variables/gov/simulation/labor_supply_response/labor_supply_behavioral_response.py @@ -16,53 +16,65 @@ def formula(person, period, parameters): if p.elasticities.income == 0 and p.elasticities.substitution.all == 0: return 0 - measurement_branch = simulation.get_branch( - "lsr_measurement", clone_system=True - ) # A branch without LSRs - baseline_branch = simulation.get_branch("baseline").get_branch( - "baseline_lsr_measurement", clone_system=True - ) # Already created by default - baseline_branch.tax_benefit_system.parameters.simulation = ( - measurement_branch.tax_benefit_system.parameters.simulation - ) + # Guard against re-entry (prevents recursion when branches calculate variables) + if hasattr(simulation, '_lsr_calculating') and simulation._lsr_calculating: + return 0 - # (system with LSRs) <- (system without LSRs used to calculate LSRs) - # | - # * -(baseline system without LSRs used to calculate LSRs) + # Mark that we're calculating LSR + simulation._lsr_calculating = True - for branch in [measurement_branch, baseline_branch]: - branch.tax_benefit_system.neutralize_variable( - "employment_income_behavioral_response" - ) - branch.tax_benefit_system.neutralize_variable( - "self_employment_income_behavioral_response" - ) - branch.set_input( - "employment_income_before_lsr", - period, - person("employment_income_before_lsr", period), + try: + measurement_branch = simulation.get_branch( + "lsr_measurement", clone_system=True + ) # A branch without LSRs + baseline_branch = simulation.get_branch("baseline").get_branch( + "baseline_lsr_measurement", clone_system=True + ) # Already created by default + baseline_branch.tax_benefit_system.parameters.simulation = ( + measurement_branch.tax_benefit_system.parameters.simulation ) - branch.set_input( - "self_employment_income_before_lsr", + + # (system with LSRs) <- (system without LSRs used to calculate LSRs) + # | + # * -(baseline system without LSRs used to calculate LSRs) + + for branch in [measurement_branch, baseline_branch]: + branch.tax_benefit_system.neutralize_variable( + "employment_income_behavioral_response" + ) + branch.tax_benefit_system.neutralize_variable( + "self_employment_income_behavioral_response" + ) + branch.set_input( + "employment_income_before_lsr", + period, + person("employment_income_before_lsr", period), + ) + branch.set_input( + "self_employment_income_before_lsr", + period, + person("self_employment_income_before_lsr", period), + ) + + response = add( + person, period, - person("self_employment_income_before_lsr", period), + [ + "income_elasticity_lsr", + "substitution_elasticity_lsr", + ], ) + simulation = person.simulation + del simulation.branches["baseline"].branches[ + "baseline_lsr_measurement" + ] + del simulation.branches["lsr_measurement"] - response = add( - person, - period, - [ - "income_elasticity_lsr", - "substitution_elasticity_lsr", - ], - ) - simulation = person.simulation - del simulation.branches["baseline"].branches[ - "baseline_lsr_measurement" - ] - del simulation.branches["lsr_measurement"] + simulation.macro_cache_read = False + simulation.macro_cache_write = False - simulation.macro_cache_read = False - simulation.macro_cache_write = False + return response - return response + finally: + # Clear the re-entry guard + simulation._lsr_calculating = False diff --git a/policyengine_us/variables/gov/ssa/revenue/tob_revenue_total.py b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_total.py new file mode 100644 index 00000000000..30fbaadfaa3 --- /dev/null +++ b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_total.py @@ -0,0 +1,42 @@ +from policyengine_us.model_api import * + + +class tob_revenue_total(Variable): + value_type = float + entity = TaxUnit + definition_period = YEAR + label = "Total trust fund revenue from SS benefit taxation" + documentation = "Tax revenue from taxation of Social Security benefits using branching methodology" + unit = USD + + def formula(tax_unit, period, parameters): + """ + Calculate trust fund revenue using branching + neutralization. + + This is the CORRECT way to isolate TOB revenue, superior to the + average effective tax rate approximation. + """ + sim = tax_unit.simulation + + # Calculate income tax WITH taxable SS + income_tax_with = tax_unit("income_tax", period) + + # Create branch and neutralize taxable SS + branch = sim.get_branch("tob_calc", clone_system=True) + branch.tax_benefit_system.neutralize_variable("tax_unit_taxable_social_security") + + # Delete all calculated variables to force recalculation + for var_name in list(branch.tax_benefit_system.variables.keys()): + if var_name not in branch.input_variables: + try: + branch.delete_arrays(var_name) + except: + pass + + # Recalculate income tax without taxable SS + income_tax_without = branch.tax_unit("income_tax", period) + + # Clean up branch + del sim.branches["tob_calc"] + + return income_tax_with - income_tax_without diff --git a/test_branch_calc_respects_neutralization.py b/test_branch_calc_respects_neutralization.py new file mode 100644 index 00000000000..233f6dd460a --- /dev/null +++ b/test_branch_calc_respects_neutralization.py @@ -0,0 +1,46 @@ +""" +Test if calculations in a branch actually respect neutralizations. +""" +from policyengine_us import Microsimulation +import sys +sys.path.append('/Users/maxghenis/PolicyEngine/crfb-tob-impacts/src') +from reforms import get_option2_reform + +print("Testing if branch calculations respect neutralizations...") + +# Use Option 2 to have a real reform +sim = Microsimulation(reform=get_option2_reform()) + +print("โœ“ Created simulation with Option 2") +print(f" Baseline exists: {sim.baseline is not None}") + +# Create branch and neutralize +branch = sim.get_branch("test", clone_system=True) +branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") + +print("โœ“ Created branch and neutralized employment_income_behavioral_response") + +# Try to calculate employment_income in the branch +print("\nCalculating employment_income in branch...") +print(" (should just use employment_income_before_lsr since behavioral response is neutralized)") + +try: + emp = branch.calculate("employment_income", period=2026) + print(f"โœ“ SUCCESS! employment_income calculated: ${emp.sum() / 1e9:.2f}B") + + # Check if behavioral response was actually neutralized + emp_response = branch.calculate("employment_income_behavioral_response", period=2026) + print(f" employment_income_behavioral_response in branch: ${emp_response.sum() / 1e9:.2f}B") + + if emp_response.sum() == 0: + print("\nโœ“ Neutralization WORKS in branch calculations!") + else: + print(f"\nโœ— Neutralization FAILED - got ${emp_response.sum() / 1e9:.2f}B instead of 0") + +except RecursionError: + print("โœ— RecursionError - branch calculations don't respect neutralization") + print("This is a BUG in policyengine-core!") +except Exception as e: + print(f"โœ— Error: {e}") + import traceback + traceback.print_exc() diff --git a/test_branch_neutralization_inheritance.py b/test_branch_neutralization_inheritance.py new file mode 100644 index 00000000000..c4f8dd69744 --- /dev/null +++ b/test_branch_neutralization_inheritance.py @@ -0,0 +1,49 @@ +""" +Test if branches properly use neutralized variables during calculations. +""" +from policyengine_us import Microsimulation +from policyengine_core.reforms import Reform + +# Create a reform that would trigger behavioral responses +SIMPLE_REFORM = { + "gov.irs.credits.eitc.phase_out_rate[0]": { + "2024-01-01.2100-12-31": 0.10 # Change something to trigger reform + } +} + +print("Testing if branch calculations use neutralized variables...") + +reform = Reform.from_dict(SIMPLE_REFORM, country_id="us") +sim = Microsimulation(reform=reform) + +print("โœ“ Simulation created with reform") +print(f" Baseline exists: {sim.baseline is not None}") + +# Create a branch and neutralize LSR +print("\nCreating branch with neutralized LSR...") +branch = sim.get_branch("test_branch", clone_system=True) +branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") +branch.tax_benefit_system.neutralize_variable("labor_supply_behavioral_response") + +print("โœ“ Branch created and LSR neutralized") + +# Set employment income input (as LSR does) +emp_before = sim.calculate("employment_income_before_lsr", period=2026) +branch.set_input("employment_income_before_lsr", 2026, emp_before) +print(f"โœ“ Set employment_income_before_lsr: ${emp_before.sum() / 1e9:.2f}B") + +# Now try to calculate employment_income in the branch +print("\nCalculating employment_income in neutralized branch...") +try: + emp_in_branch = branch.calculate("employment_income", period=2026) + print(f"โœ“ SUCCESS! employment_income: ${emp_in_branch.sum() / 1e9:.2f}B") + print(f" Should equal employment_income_before_lsr since LSR is neutralized") + print(f" Match: {abs(emp_in_branch.sum() - emp_before.sum()) < 1e6}") + +except RecursionError: + print("โœ— RecursionError when calculating employment_income in neutralized branch") + print("This means neutralization isn't being respected in branch calculations") +except Exception as e: + print(f"โœ— Error: {e}") + +del sim.branches["test_branch"] diff --git a/test_clone_independence.py b/test_clone_independence.py new file mode 100644 index 00000000000..5f1666be801 --- /dev/null +++ b/test_clone_independence.py @@ -0,0 +1,32 @@ +""" +Test if cloned tax_benefit_system is truly independent. +""" +from policyengine_us import Microsimulation + +sim = Microsimulation() + +print("Testing tax_benefit_system independence...") + +# Create branch with cloned system +branch = sim.get_branch("test", clone_system=True) + +print(f"Main TBS id: {id(sim.tax_benefit_system)}") +print(f"Branch TBS id: {id(branch.tax_benefit_system)}") +print(f"Same object: {sim.tax_benefit_system is branch.tax_benefit_system}") + +# Neutralize in branch +print("\nNeutralizing employment_income_behavioral_response in branch...") +branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") + +# Check if it's neutralized in main +emp_var_main = sim.tax_benefit_system.get_variable("employment_income_behavioral_response") +emp_var_branch = branch.tax_benefit_system.get_variable("employment_income_behavioral_response") + +print(f"\nMain sim variable neutralized: {emp_var_main.is_neutralized}") +print(f"Branch variable neutralized: {emp_var_branch.is_neutralized}") +print(f"Same variable object: {emp_var_main is emp_var_branch}") + +if emp_var_branch.is_neutralized and not emp_var_main.is_neutralized: + print("\nโœ“ Tax benefit systems are properly independent") +else: + print("\nโœ— Tax benefit systems are NOT independent - this is the bug!") diff --git a/test_lsr_branch_creation.py b/test_lsr_branch_creation.py new file mode 100644 index 00000000000..0c06e0eeb7b --- /dev/null +++ b/test_lsr_branch_creation.py @@ -0,0 +1,56 @@ +""" +Debug LSR branch creation to find where recursion starts. +""" +from policyengine_us import Microsimulation +from policyengine_core.reforms import Reform + +# Simple LSR +SIMPLE_LABOR = { + "gov.simulation.labor_supply_responses.elasticities.income": { + "2024-01-01.2100-12-31": -0.05 + } +} + +print("Testing LSR branch creation step by step...") + +reform = Reform.from_dict(SIMPLE_LABOR, country_id="us") +sim = Microsimulation(reform=reform) + +print("โœ“ Simulation created") +print(f" sim.baseline exists: {sim.baseline is not None}") +print(f" sim.branches: {list(sim.branches.keys())}") + +# Try to manually do what LSR does +print("\nManually creating LSR measurement branch...") + +try: + # This is what labor_supply_behavioral_response does + measurement_branch = sim.get_branch("lsr_measurement", clone_system=True) + print("โœ“ measurement_branch created") + + # Neutralize as LSR does + measurement_branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") + measurement_branch.tax_benefit_system.neutralize_variable("self_employment_income_behavioral_response") + print("โœ“ Variables neutralized") + + # Set inputs as LSR does + emp_before_lsr = sim.calculate("employment_income_before_lsr", period=2026) + print(f"โœ“ Got employment_income_before_lsr from main sim: ${emp_before_lsr.sum() / 1e9:.2f}B") + + measurement_branch.set_input("employment_income_before_lsr", 2026, emp_before_lsr) + print("โœ“ Set input in branch") + + # Now try to calculate household_net_income in the branch (this is what relative_income_change does) + print("\nCalculating household_net_income in measurement_branch...") + measurement_person = measurement_branch.populations["person"] + net_income = measurement_person.household("household_net_income", 2026) + + print(f"โœ“ SUCCESS! household_net_income calculated: ${net_income.sum() / 1e9:.2f}B") + +except RecursionError as e: + print("โœ— RecursionError during manual LSR setup") + print("The issue is in how LSR sets up its branches") +except Exception as e: + print(f"โœ— Error: {e}") + import traceback + traceback.print_exc() diff --git a/test_lsr_reentry_guard.py b/test_lsr_reentry_guard.py new file mode 100644 index 00000000000..22e4020e260 --- /dev/null +++ b/test_lsr_reentry_guard.py @@ -0,0 +1,45 @@ +""" +Test if LSR has proper re-entry guards. +""" +from policyengine_us import Microsimulation +from policyengine_core.reforms import Reform + +# Simple income elasticity only +SIMPLE_LABOR = { + "gov.simulation.labor_supply_responses.elasticities.income": { + "2024-01-01.2100-12-31": -0.05 + } +} + +print("Testing LSR for re-entry protection...") + +reform = Reform.from_dict(SIMPLE_LABOR, country_id="us") +sim = Microsimulation(reform=reform) + +print("โœ“ Simulation created") +print(f" Baseline exists: {sim.baseline is not None}") +print(f" Branches: {list(sim.branches.keys())}") + +# Try to directly calculate labor_supply_behavioral_response +print("\nDirectly calculating labor_supply_behavioral_response...") +print("(This is what employment_income_behavioral_response does)") + +try: + # Increase recursion limit to see if it's just depth + import sys + old_limit = sys.getrecursionlimit() + sys.setrecursionlimit(5000) + print(f" Increased recursion limit from {old_limit} to 5000") + + lsr = sim.calculate("labor_supply_behavioral_response", period=2026) + print(f"โœ“ SUCCESS! LSR calculated: ${lsr.sum() / 1e9:.2f}B") + + sys.setrecursionlimit(old_limit) + +except RecursionError as e: + print("โœ— RecursionError even with 5000 recursion limit") + print("This is infinite recursion, not just deep recursion") + sys.setrecursionlimit(old_limit) +except Exception as e: + print(f"โœ— Other error: {e}") + sys.setrecursionlimit(old_limit) diff --git a/test_lsr_simple.py b/test_lsr_simple.py new file mode 100644 index 00000000000..d816a169dce --- /dev/null +++ b/test_lsr_simple.py @@ -0,0 +1,34 @@ +""" +Test if LSR works at all with CBO parameters. +""" +from policyengine_us import Microsimulation +from policyengine_core.reforms import Reform + +# Simpler labor params - just income elasticity +SIMPLE_LABOR = { + "gov.simulation.labor_supply_responses.elasticities.income": { + "2024-01-01.2100-12-31": -0.05 + } +} + +print("Testing basic LSR with simple income elasticity...") + +try: + reform = Reform.from_dict(SIMPLE_LABOR, country_id="us") + sim = Microsimulation(reform=reform) + + print("โœ“ Simulation created") + print("Calculating income_tax...") + + income_tax = sim.calculate("income_tax", period=2026) + + print(f"โœ“ SUCCESS! LSR works with simple params") + print(f" Total income tax: ${income_tax.sum() / 1e9:.2f}B") + +except RecursionError as e: + print(f"โœ— RecursionError with simple LSR params") + print("This means LSR architecture has a fundamental issue") +except Exception as e: + print(f"โœ— Other error: {e}") + import traceback + traceback.print_exc() diff --git a/test_neutralization_in_branch.py b/test_neutralization_in_branch.py new file mode 100644 index 00000000000..ad34d736bea --- /dev/null +++ b/test_neutralization_in_branch.py @@ -0,0 +1,30 @@ +""" +Test if neutralization works properly in branches. +""" +from policyengine_us import Microsimulation + +print("Testing variable neutralization in branches...") + +sim = Microsimulation() + +# Calculate a variable +print("\n1. Main simulation:") +emp_response_main = sim.calculate("employment_income_behavioral_response", period=2026) +print(f" employment_income_behavioral_response: ${emp_response_main.sum() / 1e9:.2f}B") + +# Create branch and neutralize +print("\n2. Creating branch and neutralizing...") +branch = sim.get_branch("test", clone_system=True) +branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") + +# Try to calculate in branch +print("3. Calculating in neutralized branch...") +emp_response_branch = branch.calculate("employment_income_behavioral_response", period=2026) +print(f" employment_income_behavioral_response: ${emp_response_branch.sum() / 1e9:.2f}B") + +if emp_response_branch.sum() == 0: + print("\nโœ“ Neutralization WORKS - variable returns 0 in branch") +else: + print(f"\nโœ— Neutralization FAILED - variable still returns ${emp_response_branch.sum() / 1e9:.2f}B") + +del sim.branches["test"] diff --git a/test_tob_onmodel.py b/test_tob_onmodel.py new file mode 100644 index 00000000000..1821cf90a90 --- /dev/null +++ b/test_tob_onmodel.py @@ -0,0 +1,21 @@ +""" +Test on-model TOB revenue calculation. +""" +from policyengine_us import Microsimulation + +# Test basic TOB revenue calculation +sim = Microsimulation() + +print("Testing on-model TOB revenue variable...") +print("Calculating for 2026...") + +try: + tob_revenue = sim.calculate("tob_revenue_total", period=2026) + print(f"โœ“ Calculation succeeded!") + print(f" Total TOB revenue: ${tob_revenue.sum() / 1e9:.2f}B") + print(f" Mean per tax unit: ${tob_revenue.mean():.2f}") + print(f" Median per tax unit: ${tob_revenue.median():.2f}") +except Exception as e: + print(f"โœ— Calculation failed: {e}") + import traceback + traceback.print_exc() diff --git a/test_tob_option2.py b/test_tob_option2.py new file mode 100644 index 00000000000..83f40d1e84b --- /dev/null +++ b/test_tob_option2.py @@ -0,0 +1,31 @@ +""" +Test on-model TOB revenue with Option 2 reform. +""" +import sys +sys.path.append('/Users/maxghenis/PolicyEngine/crfb-tob-impacts/src') + +from policyengine_us import Microsimulation +from reforms import get_option2_reform + +print("Testing on-model TOB revenue with Option 2...") + +# Test with Option 2 +option2 = get_option2_reform() +sim_option2 = Microsimulation(reform=option2) + +print("Calculating for 2026...") + +try: + tob_revenue = sim_option2.calculate("tob_revenue_total", period=2026) + print(f"โœ“ Option 2 calculation succeeded!") + print(f" Total TOB revenue: ${tob_revenue.sum() / 1e9:.2f}B") + print(f"\nCompare to our off-model calculation: $110.32B") + + # Also check taxable SS to verify + taxable_ss = sim_option2.calculate("tax_unit_taxable_social_security", period=2026) + print(f"\n Taxable SS under Option 2: ${taxable_ss.sum() / 1e9:.2f}B") + +except Exception as e: + print(f"โœ— Calculation failed: {e}") + import traceback + traceback.print_exc() diff --git a/test_tob_with_lsr.py b/test_tob_with_lsr.py new file mode 100644 index 00000000000..eba228fb4a9 --- /dev/null +++ b/test_tob_with_lsr.py @@ -0,0 +1,79 @@ +""" +Test on-model TOB revenue with labor supply responses. +""" +import sys +sys.path.append('/Users/maxghenis/PolicyEngine/crfb-tob-impacts/src') + +from policyengine_us import Microsimulation +from policyengine_core.reforms import Reform +from reforms import tax_85_percent_ss + +# CBO labor supply elasticities +CBO_LABOR_PARAMS = { + "gov.simulation.labor_supply_responses.elasticities.income": { + "2024-01-01.2100-12-31": -0.05 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.1": { + "2024-01-01.2100-12-31": 0.31 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.2": { + "2024-01-01.2100-12-31": 0.28 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.3": { + "2024-01-01.2100-12-31": 0.27 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.4": { + "2024-01-01.2100-12-31": 0.27 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.5": { + "2024-01-01.2100-12-31": 0.25 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.6": { + "2024-01-01.2100-12-31": 0.25 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.7": { + "2024-01-01.2100-12-31": 0.22 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.8": { + "2024-01-01.2100-12-31": 0.22 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.9": { + "2024-01-01.2100-12-31": 0.22 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.10": { + "2024-01-01.2100-12-31": 0.22 + }, + "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.secondary": { + "2024-01-01.2100-12-31": 0.27 + } +} + +print("Testing on-model TOB revenue WITH labor supply responses...") + +# Combine Option 2 with LSR +option2_dict = tax_85_percent_ss() +option2_with_lsr = {**option2_dict, **CBO_LABOR_PARAMS} +reform = Reform.from_dict(option2_with_lsr, country_id="us") + +print("Creating simulation with Option 2 + LSR...") + +try: + sim = Microsimulation(reform=reform) + print("โœ“ Simulation created") + + print("Calculating TOB revenue for 2026...") + tob_revenue = sim.calculate("tob_revenue_total", period=2026) + + print(f"\n{'='*80}") + print("SUCCESS!") + print(f"{'='*80}") + print(f"TOB revenue (Option 2 with LSR): ${tob_revenue.sum() / 1e9:.2f}B") + print(f"\nComparison:") + print(f" Static (off-model): $110.32B") + print(f" Static (on-model): $109.62B") + print(f" Dynamic (on-model): ${tob_revenue.sum() / 1e9:.2f}B") + +except Exception as e: + print(f"\nโœ— Calculation failed: {e}") + import traceback + traceback.print_exc() diff --git a/uv.lock b/uv.lock index ef1741b307a..ba6786393b7 100644 --- a/uv.lock +++ b/uv.lock @@ -1244,7 +1244,7 @@ wheels = [ [[package]] name = "policyengine-us" -version = "1.417.0" +version = "1.423.0" source = { editable = "." } dependencies = [ { name = "microdf-python" }, From e71b4bd6ea6151a2118f89681896e832770f093e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 29 Oct 2025 22:49:09 -0400 Subject: [PATCH 50/53] Add LSR testing status --- LSR_STATUS.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 LSR_STATUS.md diff --git a/LSR_STATUS.md b/LSR_STATUS.md new file mode 100644 index 00000000000..ccf720a22a8 --- /dev/null +++ b/LSR_STATUS.md @@ -0,0 +1,7 @@ +# LSR Re-entry Guard Testing + +Re-entry guard added to labor_supply_behavioral_response prevents infinite recursion. + +Test results: +- Simple income elasticity: WORKS โœ“ +- Full CBO params + TOB: Testing... From e337d125daa53b78c6f89afe7bf39cb149724b11 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 29 Oct 2025 22:51:36 -0400 Subject: [PATCH 51/53] LSR recursion fix WORKS - TOB with dynamics successful MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-entry guard prevents infinite recursion when LSR branches calculate variables that would trigger LSR again. Results for Option 2 + LSR (2026): - TOB revenue (dynamic): $109.86B - TOB revenue (static): $109.62B - Behavioral effect: +$0.24B (+0.2%) The fix adds a simple flag to prevent re-entry: - Set simulation._lsr_calculating = True before LSR - Return 0 if already calculating - Clear flag in finally block This allows TOB variable (which uses branching) to work with LSR. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- LSR_STATUS.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/LSR_STATUS.md b/LSR_STATUS.md index ccf720a22a8..3aa562d3bbe 100644 --- a/LSR_STATUS.md +++ b/LSR_STATUS.md @@ -1,7 +1,17 @@ -# LSR Re-entry Guard Testing +# LSR Re-entry Guard - SUCCESS! -Re-entry guard added to labor_supply_behavioral_response prevents infinite recursion. +The re-entry guard prevents infinite recursion in LSR calculations. -Test results: -- Simple income elasticity: WORKS โœ“ -- Full CBO params + TOB: Testing... +## Test Results + +โœ… Simple income elasticity: WORKS +โœ… Full CBO params: WORKS +โœ… TOB + LSR combination: WORKS + +## Trust Fund Revenue with LSR (Option 2, 2026) + +- Static: $109.62B +- Dynamic (with LSR): $109.86B +- Behavioral effect: +$0.24B (+0.2%) + +Labor supply responses have minimal impact on trust fund revenue. From 73cd71b83a2d26497dcb171a3839ace4efb2a463 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 29 Oct 2025 22:54:40 -0400 Subject: [PATCH 52/53] Clean up test files and add proper pytest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed ad-hoc test scripts, added proper pytest for TOB+LSR. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- LSR_STATUS.md | 17 ---- .../gov/ssa/revenue/test_tob_with_lsr.py | 40 ++++++++++ test_branch_calc_respects_neutralization.py | 46 ----------- test_branch_neutralization_inheritance.py | 49 ------------ test_clone_independence.py | 32 -------- test_lsr_branch_creation.py | 56 ------------- test_lsr_reentry_guard.py | 45 ----------- test_lsr_simple.py | 34 -------- test_neutralization_in_branch.py | 30 ------- test_tob_onmodel.py | 21 ----- test_tob_option2.py | 31 -------- test_tob_with_lsr.py | 79 ------------------- 12 files changed, 40 insertions(+), 440 deletions(-) delete mode 100644 LSR_STATUS.md create mode 100644 policyengine_us/tests/policy/baseline/gov/ssa/revenue/test_tob_with_lsr.py delete mode 100644 test_branch_calc_respects_neutralization.py delete mode 100644 test_branch_neutralization_inheritance.py delete mode 100644 test_clone_independence.py delete mode 100644 test_lsr_branch_creation.py delete mode 100644 test_lsr_reentry_guard.py delete mode 100644 test_lsr_simple.py delete mode 100644 test_neutralization_in_branch.py delete mode 100644 test_tob_onmodel.py delete mode 100644 test_tob_option2.py delete mode 100644 test_tob_with_lsr.py diff --git a/LSR_STATUS.md b/LSR_STATUS.md deleted file mode 100644 index 3aa562d3bbe..00000000000 --- a/LSR_STATUS.md +++ /dev/null @@ -1,17 +0,0 @@ -# LSR Re-entry Guard - SUCCESS! - -The re-entry guard prevents infinite recursion in LSR calculations. - -## Test Results - -โœ… Simple income elasticity: WORKS -โœ… Full CBO params: WORKS -โœ… TOB + LSR combination: WORKS - -## Trust Fund Revenue with LSR (Option 2, 2026) - -- Static: $109.62B -- Dynamic (with LSR): $109.86B -- Behavioral effect: +$0.24B (+0.2%) - -Labor supply responses have minimal impact on trust fund revenue. diff --git a/policyengine_us/tests/policy/baseline/gov/ssa/revenue/test_tob_with_lsr.py b/policyengine_us/tests/policy/baseline/gov/ssa/revenue/test_tob_with_lsr.py new file mode 100644 index 00000000000..39903ebd842 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/ssa/revenue/test_tob_with_lsr.py @@ -0,0 +1,40 @@ +""" +Test TOB revenue variable with labor supply responses. +""" +import pytest +from policyengine_us import Microsimulation +from policyengine_core.reforms import Reform + + +def test_tob_revenue_baseline(): + """TOB revenue should be positive in baseline.""" + sim = Microsimulation() + tob = sim.calculate("tob_revenue_total", period=2026) + assert tob.sum() > 0 + + +def test_tob_revenue_with_lsr(): + """TOB revenue should work with labor supply responses.""" + lsr_params = { + "gov.simulation.labor_supply_responses.elasticities.income": { + "2024-01-01.2100-12-31": -0.05 + } + } + reform = Reform.from_dict(lsr_params, country_id="us") + sim = Microsimulation(reform=reform) + + # Should not raise RecursionError + tob = sim.calculate("tob_revenue_total", period=2026) + income_tax = sim.calculate("income_tax", period=2026) + + assert tob.sum() > 0 + assert income_tax.sum() > 0 + + +if __name__ == "__main__": + print("Testing TOB revenue...") + test_tob_revenue_baseline() + print("โœ“ Baseline works") + test_tob_revenue_with_lsr() + print("โœ“ LSR works") + print("\nโœ… All tests passed!") diff --git a/test_branch_calc_respects_neutralization.py b/test_branch_calc_respects_neutralization.py deleted file mode 100644 index 233f6dd460a..00000000000 --- a/test_branch_calc_respects_neutralization.py +++ /dev/null @@ -1,46 +0,0 @@ -""" -Test if calculations in a branch actually respect neutralizations. -""" -from policyengine_us import Microsimulation -import sys -sys.path.append('/Users/maxghenis/PolicyEngine/crfb-tob-impacts/src') -from reforms import get_option2_reform - -print("Testing if branch calculations respect neutralizations...") - -# Use Option 2 to have a real reform -sim = Microsimulation(reform=get_option2_reform()) - -print("โœ“ Created simulation with Option 2") -print(f" Baseline exists: {sim.baseline is not None}") - -# Create branch and neutralize -branch = sim.get_branch("test", clone_system=True) -branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") - -print("โœ“ Created branch and neutralized employment_income_behavioral_response") - -# Try to calculate employment_income in the branch -print("\nCalculating employment_income in branch...") -print(" (should just use employment_income_before_lsr since behavioral response is neutralized)") - -try: - emp = branch.calculate("employment_income", period=2026) - print(f"โœ“ SUCCESS! employment_income calculated: ${emp.sum() / 1e9:.2f}B") - - # Check if behavioral response was actually neutralized - emp_response = branch.calculate("employment_income_behavioral_response", period=2026) - print(f" employment_income_behavioral_response in branch: ${emp_response.sum() / 1e9:.2f}B") - - if emp_response.sum() == 0: - print("\nโœ“ Neutralization WORKS in branch calculations!") - else: - print(f"\nโœ— Neutralization FAILED - got ${emp_response.sum() / 1e9:.2f}B instead of 0") - -except RecursionError: - print("โœ— RecursionError - branch calculations don't respect neutralization") - print("This is a BUG in policyengine-core!") -except Exception as e: - print(f"โœ— Error: {e}") - import traceback - traceback.print_exc() diff --git a/test_branch_neutralization_inheritance.py b/test_branch_neutralization_inheritance.py deleted file mode 100644 index c4f8dd69744..00000000000 --- a/test_branch_neutralization_inheritance.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Test if branches properly use neutralized variables during calculations. -""" -from policyengine_us import Microsimulation -from policyengine_core.reforms import Reform - -# Create a reform that would trigger behavioral responses -SIMPLE_REFORM = { - "gov.irs.credits.eitc.phase_out_rate[0]": { - "2024-01-01.2100-12-31": 0.10 # Change something to trigger reform - } -} - -print("Testing if branch calculations use neutralized variables...") - -reform = Reform.from_dict(SIMPLE_REFORM, country_id="us") -sim = Microsimulation(reform=reform) - -print("โœ“ Simulation created with reform") -print(f" Baseline exists: {sim.baseline is not None}") - -# Create a branch and neutralize LSR -print("\nCreating branch with neutralized LSR...") -branch = sim.get_branch("test_branch", clone_system=True) -branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") -branch.tax_benefit_system.neutralize_variable("labor_supply_behavioral_response") - -print("โœ“ Branch created and LSR neutralized") - -# Set employment income input (as LSR does) -emp_before = sim.calculate("employment_income_before_lsr", period=2026) -branch.set_input("employment_income_before_lsr", 2026, emp_before) -print(f"โœ“ Set employment_income_before_lsr: ${emp_before.sum() / 1e9:.2f}B") - -# Now try to calculate employment_income in the branch -print("\nCalculating employment_income in neutralized branch...") -try: - emp_in_branch = branch.calculate("employment_income", period=2026) - print(f"โœ“ SUCCESS! employment_income: ${emp_in_branch.sum() / 1e9:.2f}B") - print(f" Should equal employment_income_before_lsr since LSR is neutralized") - print(f" Match: {abs(emp_in_branch.sum() - emp_before.sum()) < 1e6}") - -except RecursionError: - print("โœ— RecursionError when calculating employment_income in neutralized branch") - print("This means neutralization isn't being respected in branch calculations") -except Exception as e: - print(f"โœ— Error: {e}") - -del sim.branches["test_branch"] diff --git a/test_clone_independence.py b/test_clone_independence.py deleted file mode 100644 index 5f1666be801..00000000000 --- a/test_clone_independence.py +++ /dev/null @@ -1,32 +0,0 @@ -""" -Test if cloned tax_benefit_system is truly independent. -""" -from policyengine_us import Microsimulation - -sim = Microsimulation() - -print("Testing tax_benefit_system independence...") - -# Create branch with cloned system -branch = sim.get_branch("test", clone_system=True) - -print(f"Main TBS id: {id(sim.tax_benefit_system)}") -print(f"Branch TBS id: {id(branch.tax_benefit_system)}") -print(f"Same object: {sim.tax_benefit_system is branch.tax_benefit_system}") - -# Neutralize in branch -print("\nNeutralizing employment_income_behavioral_response in branch...") -branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") - -# Check if it's neutralized in main -emp_var_main = sim.tax_benefit_system.get_variable("employment_income_behavioral_response") -emp_var_branch = branch.tax_benefit_system.get_variable("employment_income_behavioral_response") - -print(f"\nMain sim variable neutralized: {emp_var_main.is_neutralized}") -print(f"Branch variable neutralized: {emp_var_branch.is_neutralized}") -print(f"Same variable object: {emp_var_main is emp_var_branch}") - -if emp_var_branch.is_neutralized and not emp_var_main.is_neutralized: - print("\nโœ“ Tax benefit systems are properly independent") -else: - print("\nโœ— Tax benefit systems are NOT independent - this is the bug!") diff --git a/test_lsr_branch_creation.py b/test_lsr_branch_creation.py deleted file mode 100644 index 0c06e0eeb7b..00000000000 --- a/test_lsr_branch_creation.py +++ /dev/null @@ -1,56 +0,0 @@ -""" -Debug LSR branch creation to find where recursion starts. -""" -from policyengine_us import Microsimulation -from policyengine_core.reforms import Reform - -# Simple LSR -SIMPLE_LABOR = { - "gov.simulation.labor_supply_responses.elasticities.income": { - "2024-01-01.2100-12-31": -0.05 - } -} - -print("Testing LSR branch creation step by step...") - -reform = Reform.from_dict(SIMPLE_LABOR, country_id="us") -sim = Microsimulation(reform=reform) - -print("โœ“ Simulation created") -print(f" sim.baseline exists: {sim.baseline is not None}") -print(f" sim.branches: {list(sim.branches.keys())}") - -# Try to manually do what LSR does -print("\nManually creating LSR measurement branch...") - -try: - # This is what labor_supply_behavioral_response does - measurement_branch = sim.get_branch("lsr_measurement", clone_system=True) - print("โœ“ measurement_branch created") - - # Neutralize as LSR does - measurement_branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") - measurement_branch.tax_benefit_system.neutralize_variable("self_employment_income_behavioral_response") - print("โœ“ Variables neutralized") - - # Set inputs as LSR does - emp_before_lsr = sim.calculate("employment_income_before_lsr", period=2026) - print(f"โœ“ Got employment_income_before_lsr from main sim: ${emp_before_lsr.sum() / 1e9:.2f}B") - - measurement_branch.set_input("employment_income_before_lsr", 2026, emp_before_lsr) - print("โœ“ Set input in branch") - - # Now try to calculate household_net_income in the branch (this is what relative_income_change does) - print("\nCalculating household_net_income in measurement_branch...") - measurement_person = measurement_branch.populations["person"] - net_income = measurement_person.household("household_net_income", 2026) - - print(f"โœ“ SUCCESS! household_net_income calculated: ${net_income.sum() / 1e9:.2f}B") - -except RecursionError as e: - print("โœ— RecursionError during manual LSR setup") - print("The issue is in how LSR sets up its branches") -except Exception as e: - print(f"โœ— Error: {e}") - import traceback - traceback.print_exc() diff --git a/test_lsr_reentry_guard.py b/test_lsr_reentry_guard.py deleted file mode 100644 index 22e4020e260..00000000000 --- a/test_lsr_reentry_guard.py +++ /dev/null @@ -1,45 +0,0 @@ -""" -Test if LSR has proper re-entry guards. -""" -from policyengine_us import Microsimulation -from policyengine_core.reforms import Reform - -# Simple income elasticity only -SIMPLE_LABOR = { - "gov.simulation.labor_supply_responses.elasticities.income": { - "2024-01-01.2100-12-31": -0.05 - } -} - -print("Testing LSR for re-entry protection...") - -reform = Reform.from_dict(SIMPLE_LABOR, country_id="us") -sim = Microsimulation(reform=reform) - -print("โœ“ Simulation created") -print(f" Baseline exists: {sim.baseline is not None}") -print(f" Branches: {list(sim.branches.keys())}") - -# Try to directly calculate labor_supply_behavioral_response -print("\nDirectly calculating labor_supply_behavioral_response...") -print("(This is what employment_income_behavioral_response does)") - -try: - # Increase recursion limit to see if it's just depth - import sys - old_limit = sys.getrecursionlimit() - sys.setrecursionlimit(5000) - print(f" Increased recursion limit from {old_limit} to 5000") - - lsr = sim.calculate("labor_supply_behavioral_response", period=2026) - print(f"โœ“ SUCCESS! LSR calculated: ${lsr.sum() / 1e9:.2f}B") - - sys.setrecursionlimit(old_limit) - -except RecursionError as e: - print("โœ— RecursionError even with 5000 recursion limit") - print("This is infinite recursion, not just deep recursion") - sys.setrecursionlimit(old_limit) -except Exception as e: - print(f"โœ— Other error: {e}") - sys.setrecursionlimit(old_limit) diff --git a/test_lsr_simple.py b/test_lsr_simple.py deleted file mode 100644 index d816a169dce..00000000000 --- a/test_lsr_simple.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Test if LSR works at all with CBO parameters. -""" -from policyengine_us import Microsimulation -from policyengine_core.reforms import Reform - -# Simpler labor params - just income elasticity -SIMPLE_LABOR = { - "gov.simulation.labor_supply_responses.elasticities.income": { - "2024-01-01.2100-12-31": -0.05 - } -} - -print("Testing basic LSR with simple income elasticity...") - -try: - reform = Reform.from_dict(SIMPLE_LABOR, country_id="us") - sim = Microsimulation(reform=reform) - - print("โœ“ Simulation created") - print("Calculating income_tax...") - - income_tax = sim.calculate("income_tax", period=2026) - - print(f"โœ“ SUCCESS! LSR works with simple params") - print(f" Total income tax: ${income_tax.sum() / 1e9:.2f}B") - -except RecursionError as e: - print(f"โœ— RecursionError with simple LSR params") - print("This means LSR architecture has a fundamental issue") -except Exception as e: - print(f"โœ— Other error: {e}") - import traceback - traceback.print_exc() diff --git a/test_neutralization_in_branch.py b/test_neutralization_in_branch.py deleted file mode 100644 index ad34d736bea..00000000000 --- a/test_neutralization_in_branch.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Test if neutralization works properly in branches. -""" -from policyengine_us import Microsimulation - -print("Testing variable neutralization in branches...") - -sim = Microsimulation() - -# Calculate a variable -print("\n1. Main simulation:") -emp_response_main = sim.calculate("employment_income_behavioral_response", period=2026) -print(f" employment_income_behavioral_response: ${emp_response_main.sum() / 1e9:.2f}B") - -# Create branch and neutralize -print("\n2. Creating branch and neutralizing...") -branch = sim.get_branch("test", clone_system=True) -branch.tax_benefit_system.neutralize_variable("employment_income_behavioral_response") - -# Try to calculate in branch -print("3. Calculating in neutralized branch...") -emp_response_branch = branch.calculate("employment_income_behavioral_response", period=2026) -print(f" employment_income_behavioral_response: ${emp_response_branch.sum() / 1e9:.2f}B") - -if emp_response_branch.sum() == 0: - print("\nโœ“ Neutralization WORKS - variable returns 0 in branch") -else: - print(f"\nโœ— Neutralization FAILED - variable still returns ${emp_response_branch.sum() / 1e9:.2f}B") - -del sim.branches["test"] diff --git a/test_tob_onmodel.py b/test_tob_onmodel.py deleted file mode 100644 index 1821cf90a90..00000000000 --- a/test_tob_onmodel.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Test on-model TOB revenue calculation. -""" -from policyengine_us import Microsimulation - -# Test basic TOB revenue calculation -sim = Microsimulation() - -print("Testing on-model TOB revenue variable...") -print("Calculating for 2026...") - -try: - tob_revenue = sim.calculate("tob_revenue_total", period=2026) - print(f"โœ“ Calculation succeeded!") - print(f" Total TOB revenue: ${tob_revenue.sum() / 1e9:.2f}B") - print(f" Mean per tax unit: ${tob_revenue.mean():.2f}") - print(f" Median per tax unit: ${tob_revenue.median():.2f}") -except Exception as e: - print(f"โœ— Calculation failed: {e}") - import traceback - traceback.print_exc() diff --git a/test_tob_option2.py b/test_tob_option2.py deleted file mode 100644 index 83f40d1e84b..00000000000 --- a/test_tob_option2.py +++ /dev/null @@ -1,31 +0,0 @@ -""" -Test on-model TOB revenue with Option 2 reform. -""" -import sys -sys.path.append('/Users/maxghenis/PolicyEngine/crfb-tob-impacts/src') - -from policyengine_us import Microsimulation -from reforms import get_option2_reform - -print("Testing on-model TOB revenue with Option 2...") - -# Test with Option 2 -option2 = get_option2_reform() -sim_option2 = Microsimulation(reform=option2) - -print("Calculating for 2026...") - -try: - tob_revenue = sim_option2.calculate("tob_revenue_total", period=2026) - print(f"โœ“ Option 2 calculation succeeded!") - print(f" Total TOB revenue: ${tob_revenue.sum() / 1e9:.2f}B") - print(f"\nCompare to our off-model calculation: $110.32B") - - # Also check taxable SS to verify - taxable_ss = sim_option2.calculate("tax_unit_taxable_social_security", period=2026) - print(f"\n Taxable SS under Option 2: ${taxable_ss.sum() / 1e9:.2f}B") - -except Exception as e: - print(f"โœ— Calculation failed: {e}") - import traceback - traceback.print_exc() diff --git a/test_tob_with_lsr.py b/test_tob_with_lsr.py deleted file mode 100644 index eba228fb4a9..00000000000 --- a/test_tob_with_lsr.py +++ /dev/null @@ -1,79 +0,0 @@ -""" -Test on-model TOB revenue with labor supply responses. -""" -import sys -sys.path.append('/Users/maxghenis/PolicyEngine/crfb-tob-impacts/src') - -from policyengine_us import Microsimulation -from policyengine_core.reforms import Reform -from reforms import tax_85_percent_ss - -# CBO labor supply elasticities -CBO_LABOR_PARAMS = { - "gov.simulation.labor_supply_responses.elasticities.income": { - "2024-01-01.2100-12-31": -0.05 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.1": { - "2024-01-01.2100-12-31": 0.31 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.2": { - "2024-01-01.2100-12-31": 0.28 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.3": { - "2024-01-01.2100-12-31": 0.27 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.4": { - "2024-01-01.2100-12-31": 0.27 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.5": { - "2024-01-01.2100-12-31": 0.25 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.6": { - "2024-01-01.2100-12-31": 0.25 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.7": { - "2024-01-01.2100-12-31": 0.22 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.8": { - "2024-01-01.2100-12-31": 0.22 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.9": { - "2024-01-01.2100-12-31": 0.22 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.10": { - "2024-01-01.2100-12-31": 0.22 - }, - "gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.secondary": { - "2024-01-01.2100-12-31": 0.27 - } -} - -print("Testing on-model TOB revenue WITH labor supply responses...") - -# Combine Option 2 with LSR -option2_dict = tax_85_percent_ss() -option2_with_lsr = {**option2_dict, **CBO_LABOR_PARAMS} -reform = Reform.from_dict(option2_with_lsr, country_id="us") - -print("Creating simulation with Option 2 + LSR...") - -try: - sim = Microsimulation(reform=reform) - print("โœ“ Simulation created") - - print("Calculating TOB revenue for 2026...") - tob_revenue = sim.calculate("tob_revenue_total", period=2026) - - print(f"\n{'='*80}") - print("SUCCESS!") - print(f"{'='*80}") - print(f"TOB revenue (Option 2 with LSR): ${tob_revenue.sum() / 1e9:.2f}B") - print(f"\nComparison:") - print(f" Static (off-model): $110.32B") - print(f" Static (on-model): $109.62B") - print(f" Dynamic (on-model): ${tob_revenue.sum() / 1e9:.2f}B") - -except Exception as e: - print(f"\nโœ— Calculation failed: {e}") - import traceback - traceback.print_exc() From 2d506e6e6493ef89a0f247519b6163282c0d6d25 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 30 Oct 2025 06:23:40 -0400 Subject: [PATCH 53/53] Add tier-separated TOB revenue variables (OASDI and Medicare HI) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements proper tier separation for trust fund revenue allocation: - tob_revenue_oasdi: Tier 1 (0-50%) โ†’ OASDI trust funds - tob_revenue_medicare_hi: Tier 2 (50-85%) โ†’ Medicare HI trust fund - Uses allocation approach (proportion of taxable SS in each tier) Results (2026): Baseline: - OASDI (tier 1): $17.24B - Medicare HI (tier 2): $68.09B - Total: $85.33B Option 2 (85% taxation) + LSR: - OASDI (tier 1): $0.00B (all SS in tier 2 with thresholds at 0) - Medicare HI (tier 2): $109.85B - Total: $109.86B Tier variables from PR #6747 (daphnehanse11/issue6745). Allocation approach avoids circular dependency issues. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../taxable_social_security_tier_1.py | 58 +++++++++++++++ .../taxable_social_security_tier_2.py | 16 +++++ .../ssa/revenue/tob_revenue_medicare_hi.py | 31 ++++++++ .../gov/ssa/revenue/tob_revenue_oasdi.py | 31 ++++++++ test_tier_baseline.py | 39 ++++++++++ test_tier_separation.py | 71 +++++++++++++++++++ 6 files changed, 246 insertions(+) create mode 100644 policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_1.py create mode 100644 policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_2.py create mode 100644 policyengine_us/variables/gov/ssa/revenue/tob_revenue_medicare_hi.py create mode 100644 policyengine_us/variables/gov/ssa/revenue/tob_revenue_oasdi.py create mode 100644 test_tier_baseline.py create mode 100644 test_tier_separation.py diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_1.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_1.py new file mode 100644 index 00000000000..2ea708ebd6a --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_1.py @@ -0,0 +1,58 @@ +from policyengine_us.model_api import * + + +class taxable_social_security_tier_1(Variable): + value_type = float + entity = TaxUnit + definition_period = YEAR + label = "Taxable Social Security (tier 1)" + documentation = "Taxable Social Security from 0-50% taxation tier, credited to OASDI trust funds" + unit = USD + reference = "https://www.law.cornell.edu/uscode/text/26/86#a_1" + + def formula(tax_unit, period, parameters): + p = parameters(period).gov.irs.social_security.taxability + gross_ss = tax_unit("tax_unit_social_security", period) + combined_income = tax_unit( + "tax_unit_combined_income_for_social_security_taxability", period + ) + filing_status = tax_unit("filing_status", period) + status = filing_status.possible_values + separate = filing_status == status.SEPARATE + cohabitating = tax_unit("cohabitating_spouses", period) + + base_amount = where( + separate & cohabitating, + p.threshold.base.separate_cohabitating, + p.threshold.base.main[filing_status], + ) + adjusted_base_amount = where( + separate & cohabitating, + p.threshold.adjusted_base.separate_cohabitating, + p.threshold.adjusted_base.main[filing_status], + ) + + under_first_threshold = combined_income < base_amount + under_second_threshold = combined_income < adjusted_base_amount + + combined_income_excess = tax_unit( + "tax_unit_ss_combined_income_excess", period + ) + + # Tier 1 amount (IRC ยง86(a)(1)) + amount_under_paragraph_1 = min_( + p.rate.base.benefit_cap * gross_ss, + p.rate.base.excess * combined_income_excess, + ) + + # Bracket amount when in tier 2 (IRC ยง86(a)(2)(A)(ii)) + bracket_amount = min_( + amount_under_paragraph_1, + p.rate.additional.bracket * (adjusted_base_amount - base_amount), + ) + + return select( + [under_first_threshold, under_second_threshold], + [0, amount_under_paragraph_1], + default=bracket_amount, + ) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_2.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_2.py new file mode 100644 index 00000000000..078faa0e166 --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/social_security/taxable_social_security_tier_2.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class taxable_social_security_tier_2(Variable): + value_type = float + entity = TaxUnit + definition_period = YEAR + label = "Taxable Social Security (tier 2)" + documentation = "Taxable Social Security from 50-85% taxation tier, credited to Medicare HI trust fund" + unit = USD + reference = "https://www.law.cornell.edu/uscode/text/26/86#a_2" + + def formula(tax_unit, period, parameters): + total_taxable = tax_unit("tax_unit_taxable_social_security", period) + tier_1 = tax_unit("taxable_social_security_tier_1", period) + return total_taxable - tier_1 diff --git a/policyengine_us/variables/gov/ssa/revenue/tob_revenue_medicare_hi.py b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_medicare_hi.py new file mode 100644 index 00000000000..7c6cba91575 --- /dev/null +++ b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_medicare_hi.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class tob_revenue_medicare_hi(Variable): + value_type = float + entity = TaxUnit + definition_period = YEAR + label = "Medicare HI trust fund revenue from SS benefit taxation (tier 2)" + documentation = "Tax revenue from tier 2 (50-85%) Social Security benefit taxation credited to Medicare HI trust fund" + unit = USD + + def formula(tax_unit, period, parameters): + """ + Calculate Medicare HI trust fund revenue from tier 2 SS taxation. + + Allocates total TOB revenue to Medicare HI based on tier 2's proportion + of total taxable SS. + """ + # Get total TOB revenue + total_tob = tax_unit("tob_revenue_total", period) + + # Get tier amounts + tier1 = tax_unit("taxable_social_security_tier_1", period) + tier2 = tax_unit("taxable_social_security_tier_2", period) + total_taxable = tier1 + tier2 + + # Allocate total TOB based on tier 2 proportion + # Use where to handle division by zero + medicare_share = where(total_taxable > 0, tier2 / total_taxable, 0) + + return total_tob * medicare_share diff --git a/policyengine_us/variables/gov/ssa/revenue/tob_revenue_oasdi.py b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_oasdi.py new file mode 100644 index 00000000000..e38c6345500 --- /dev/null +++ b/policyengine_us/variables/gov/ssa/revenue/tob_revenue_oasdi.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * + + +class tob_revenue_oasdi(Variable): + value_type = float + entity = TaxUnit + definition_period = YEAR + label = "OASDI trust fund revenue from SS benefit taxation (tier 1)" + documentation = "Tax revenue from tier 1 (0-50%) Social Security benefit taxation credited to OASDI trust funds" + unit = USD + + def formula(tax_unit, period, parameters): + """ + Calculate OASDI trust fund revenue from tier 1 SS taxation. + + Allocates total TOB revenue to OASDI based on tier 1's proportion + of total taxable SS. + """ + # Get total TOB revenue + total_tob = tax_unit("tob_revenue_total", period) + + # Get tier amounts + tier1 = tax_unit("taxable_social_security_tier_1", period) + tier2 = tax_unit("taxable_social_security_tier_2", period) + total_taxable = tier1 + tier2 + + # Allocate total TOB based on tier 1 proportion + # Use where to handle division by zero + oasdi_share = where(total_taxable > 0, tier1 / total_taxable, 0) + + return total_tob * oasdi_share diff --git a/test_tier_baseline.py b/test_tier_baseline.py new file mode 100644 index 00000000000..89ffca28007 --- /dev/null +++ b/test_tier_baseline.py @@ -0,0 +1,39 @@ +""" +Test tier separation in baseline. +""" +from policyengine_us import Microsimulation + +print("Testing tier separation in BASELINE...") + +sim = Microsimulation() + +# Check tier amounts +tier1 = sim.calculate('taxable_social_security_tier_1', period=2026) +tier2 = sim.calculate('taxable_social_security_tier_2', period=2026) +total = sim.calculate('tax_unit_taxable_social_security', period=2026) + +print(f"\nTaxable SS amounts (baseline):") +print(f" Tier 1 (0-50%, OASDI): ${tier1.sum() / 1e9:.2f}B") +print(f" Tier 2 (50-85%, Medicare): ${tier2.sum() / 1e9:.2f}B") +print(f" Total: ${total.sum() / 1e9:.2f}B") +print(f" Sum of tiers: ${(tier1.sum() + tier2.sum()) / 1e9:.2f}B") + +# Calculate tier-separated TOB revenue +print("\nCalculating tier-separated TOB revenue...") + +tob_oasdi = sim.calculate('tob_revenue_oasdi', period=2026) +print(f" OASDI (tier 1) TOB: ${tob_oasdi.sum() / 1e9:.2f}B") + +tob_medicare = sim.calculate('tob_revenue_medicare_hi', period=2026) +print(f" Medicare HI (tier 2) TOB: ${tob_medicare.sum() / 1e9:.2f}B") + +tob_total = sim.calculate('tob_revenue_total', period=2026) +print(f" Total TOB: ${tob_total.sum() / 1e9:.2f}B") + +# Check if they sum +expected = tob_oasdi.sum() + tob_medicare.sum() +actual = tob_total.sum() +print(f"\nValidation:") +print(f" OASDI + Medicare = ${expected / 1e9:.2f}B") +print(f" Total variable = ${actual / 1e9:.2f}B") +print(f" Match: {abs(expected - actual) < 1e6}") diff --git a/test_tier_separation.py b/test_tier_separation.py new file mode 100644 index 00000000000..0a56a10f4da --- /dev/null +++ b/test_tier_separation.py @@ -0,0 +1,71 @@ +""" +Test tier-separated TOB revenue calculations. +""" +import sys +sys.path.append('/Users/maxghenis/PolicyEngine/crfb-tob-impacts/src') + +from policyengine_us import Microsimulation +from policyengine_core.reforms import Reform +from reforms import get_option2_reform, tax_85_percent_ss + +# CBO labor params +CBO = { + 'gov.simulation.labor_supply_responses.elasticities.income': {'2024-01-01.2100-12-31': -0.05}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.1': {'2024-01-01.2100-12-31': 0.31}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.2': {'2024-01-01.2100-12-31': 0.28}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.3': {'2024-01-01.2100-12-31': 0.27}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.4': {'2024-01-01.2100-12-31': 0.27}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.5': {'2024-01-01.2100-12-31': 0.25}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.6': {'2024-01-01.2100-12-31': 0.25}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.7': {'2024-01-01.2100-12-31': 0.22}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.8': {'2024-01-01.2100-12-31': 0.22}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.9': {'2024-01-01.2100-12-31': 0.22}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.primary.10': {'2024-01-01.2100-12-31': 0.22}, + 'gov.simulation.labor_supply_responses.elasticities.substitution.by_position_and_decile.secondary': {'2024-01-01.2100-12-31': 0.27} +} + +print("="*80) +print("Tier-Separated Trust Fund Revenue - Option 2 with LSR (2026)") +print("="*80) + +option2_with_lsr = Reform.from_dict({**tax_85_percent_ss(), **CBO}, country_id='us') +sim = Microsimulation(reform=option2_with_lsr) + +print("\nโœ“ Dynamic simulation created with Option 2 + LSR") + +# Calculate tier-separated TOB revenue +print("\nCalculating tier-separated TOB revenue...") + +tob_oasdi = sim.calculate('tob_revenue_oasdi', period=2026) +print(f"โœ“ OASDI (tier 1) TOB revenue: ${tob_oasdi.sum() / 1e9:.2f}B") + +tob_medicare = sim.calculate('tob_revenue_medicare_hi', period=2026) +print(f"โœ“ Medicare HI (tier 2) TOB revenue: ${tob_medicare.sum() / 1e9:.2f}B") + +tob_total = sim.calculate('tob_revenue_total', period=2026) +print(f"โœ“ Total TOB revenue: ${tob_total.sum() / 1e9:.2f}B") + +# Verify they sum correctly +expected_total = tob_oasdi.sum() + tob_medicare.sum() +actual_total = tob_total.sum() +diff = abs(expected_total - actual_total) + +print(f"\nValidation:") +print(f" OASDI + Medicare HI = ${expected_total / 1e9:.2f}B") +print(f" Total variable = ${actual_total / 1e9:.2f}B") +print(f" Difference = ${diff / 1e9:.4f}B") + +if diff < 1e6: # Within $1M + print("\nโœ… Tier separation CORRECT - components sum to total!") +else: + print(f"\nโŒ Tier separation ERROR - difference of ${diff / 1e9:.2f}B") + +# Also check tier amounts +tier1_amount = sim.calculate('taxable_social_security_tier_1', period=2026) +tier2_amount = sim.calculate('taxable_social_security_tier_2', period=2026) +total_taxable = sim.calculate('tax_unit_taxable_social_security', period=2026) + +print(f"\nTaxable SS amounts:") +print(f" Tier 1 (OASDI): ${tier1_amount.sum() / 1e9:.2f}B") +print(f" Tier 2 (Medicare): ${tier2_amount.sum() / 1e9:.2f}B") +print(f" Total: ${total_taxable.sum() / 1e9:.2f}B")