From 385a48dea0f9bb687e45665e29cce6929c470d22 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 24 Mar 2026 18:42:58 -0400 Subject: [PATCH 1/7] Backdate TANF non-cash BBCE parameters to FY2016 and fix CO bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pushes 5 TANF non-cash (BBCE) parameter start dates from 2020-07-01 back to 2015-10-01, using USDA ERS SNAP Policy Database (monthly state-by-state data 1996-2020) and SNAP Screener data for net income test values. Adds 8 mid-period state changes: - IL Jan 2016: gross income 130% → 165% - IN Jan 2018: adopted BBCE (130%, $5K asset, net test) - CO Jun 2018: gross income 130% → 200% - WV Jul 2018: gross income 130% → 200%, requires-all → any - MS Jul 2019: dropped BBCE - MI Dec 2019: asset limit $5K → $15K - LA Apr 2020: adopted BBCE (130%, no asset limit, net test) - KY May 2020: gross income 130% → 200% Fixes CO bug: was marked as non-BBCE (-.inf) at 2020-07-01 but ERS data shows CO had BBCE with 130% since at least Oct 2015, increasing to 200% in Jun 2018. Co-Authored-By: Claude Opus 4.6 (1M context) --- changelog.d/backdate-snap-bbce.added.md | 1 + .../gov/hhs/tanf/non_cash/asset_limit.yaml | 105 ++++++++-------- .../hhs/tanf/non_cash/income_limit/gross.yaml | 113 ++++++++++-------- .../income_limit/net_applies/hheod.yaml | 88 +++++++------- .../income_limit/net_applies/non_hheod.yaml | 88 +++++++------- .../tanf/non_cash/requires_all_for_hheod.yaml | 29 +++-- 6 files changed, 219 insertions(+), 205 deletions(-) create mode 100644 changelog.d/backdate-snap-bbce.added.md diff --git a/changelog.d/backdate-snap-bbce.added.md b/changelog.d/backdate-snap-bbce.added.md new file mode 100644 index 00000000000..29494c1542f --- /dev/null +++ b/changelog.d/backdate-snap-bbce.added.md @@ -0,0 +1 @@ +Backdate TANF non-cash BBCE parameters to FY2016 and fix CO BBCE bug. diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml index 660ea617b62..a084ecf2757 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml @@ -6,19 +6,19 @@ AL: # limit. # This is not yet modeled. # Other states with this rule are marked with a *. - 2020-07-01: .inf + 2015-10-01: .inf AK: # Alaska is not in the list, indicating it has not elected to BBCE. # Non-BBCE states are entered as negative infinity asset limit. - 2020-07-01: -.inf + 2015-10-01: -.inf AZ: - 2020-07-01: .inf + 2015-10-01: .inf AR: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. CA: # * - 2020-07-01: .inf + 2015-10-01: .inf CO: # * - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: .inf # May have changed prior to 2022-11-30. 2022-11-30: value: .inf @@ -27,97 +27,100 @@ CO: # * - title: Code of Colorado Regulations | Rule Manual Volume 4, SNAP | 4.206(C)(2)(b) (page 35) href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=10492&fileName=10%20CCR%202506-1 CT: - 2020-07-01: .inf + 2015-10-01: .inf DE: - 2020-07-01: .inf + 2015-10-01: .inf DC: - 2020-07-01: .inf + 2015-10-01: .inf FL: # * - 2020-07-01: .inf + 2015-10-01: .inf GA: # * - 2020-07-01: .inf + 2015-10-01: .inf GU: - 2020-07-01: .inf + 2015-10-01: .inf HI: - 2020-07-01: .inf + 2015-10-01: .inf ID: - 2020-07-01: 5_000 + 2015-10-01: 5_000 IL: # * - 2020-07-01: .inf + 2015-10-01: .inf IN: - 2020-07-01: 5_000 + 2015-10-01: -.inf + 2018-01-01: 5_000 IA: - 2020-07-01: .inf + 2015-10-01: .inf KS: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. KY: # * - 2020-07-01: .inf + 2015-10-01: .inf LA: - 2020-07-01: .inf + 2015-10-01: -.inf + 2020-04-01: .inf ME: - 2020-07-01: .inf + 2015-10-01: .inf MD: - 2020-07-01: .inf + 2015-10-01: .inf MA: # * - 2020-07-01: .inf + 2015-10-01: .inf MI: - 2020-07-01: 15_000 + 2015-10-01: 5_000 + 2019-12-01: 15_000 2023-07-01: .inf MN: - 2020-07-01: .inf + 2015-10-01: .inf MS: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. MT: - 2020-07-01: .inf + 2015-10-01: .inf NE: - 2020-07-01: 25_000 # For liquid assets (not modeled). + 2015-10-01: 25_000 # For liquid assets (not modeled). NV: - 2020-07-01: .inf + 2015-10-01: .inf NH: # Only households with at least one dependent child and a specified relative to that child. # (Not currently modeled.) - 2020-07-01: .inf + 2015-10-01: .inf NJ: - 2020-07-01: .inf + 2015-10-01: .inf NM: - 2020-07-01: .inf + 2015-10-01: .inf NY: # * # Only for households with dependent care expenses or earned income (not currently implemented). - 2020-07-01: .inf + 2015-10-01: .inf NC: - 2020-07-01: .inf + 2015-10-01: .inf ND: - 2020-07-01: .inf + 2015-10-01: .inf OH: # * - 2020-07-01: .inf + 2015-10-01: .inf OK: - 2020-07-01: .inf + 2015-10-01: .inf OR: - 2020-07-01: .inf + 2015-10-01: .inf PA: # * - 2020-07-01: .inf + 2015-10-01: .inf RI: # * - 2020-07-01: .inf + 2015-10-01: .inf SC: # * - 2020-07-01: .inf + 2015-10-01: .inf TX: - 2020-07-01: 5_000 # Excludes 1 vehicle up to $15,000 & includes excess vehicle value (not modeled). + 2015-10-01: 5_000 # Excludes 1 vehicle up to $15,000 & includes excess vehicle value (not modeled). UT: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. VT: - 2020-07-01: .inf + 2015-10-01: .inf VI: # * - 2020-07-01: .inf + 2015-10-01: .inf VA: - 2020-07-01: .inf + 2015-10-01: .inf WA: - 2020-07-01: .inf + 2015-10-01: .inf WV: # * - 2020-07-01: .inf + 2015-10-01: .inf WI: - 2020-07-01: .inf + 2015-10-01: .inf WY: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. metadata: label: TANF non-cash asset limit for SNAP BBCE @@ -125,6 +128,6 @@ metadata: reference: - title: "USDA: Broad-based Categorical Eligibility" href: https://fns-prod.azureedge.net/sites/default/files/resource-files/BBCE%20States%20Chart%20(July%202020).pdf - date: 2020-07-01 + date: 2015-10-01 breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml index 0d35156b487..3f7d844a7ce 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml @@ -1,20 +1,21 @@ description: Gross income limit as a percent of the poverty line for TANF non-cash benefit used for SNAP BBCE, by state. AL: - 2020-07-01: 1.3 + 2015-10-01: 1.3 AK: # Alaska is not in the list, indicating it has not elected to BBCE. # Non-BBCE states are entered as negative .infinity asset limit. - 2020-07-01: -.inf + 2015-10-01: -.inf 2025-07-01: 2 AZ: - 2020-07-01: 1.85 + 2015-10-01: 1.85 AR: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. CA: - 2020-07-01: 2 + 2015-10-01: 2 CO: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: 1.3 + 2018-06-01: 2 # Maybe have changed prior to 2022-11-30. 2022-11-30: value: 2 @@ -23,115 +24,121 @@ CO: - title: Code of Colorado Regulations | Rule Manual Volume 4, SNAP | 4.206(C)(2)(a)(1) (page 34) href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=10492&fileName=10%20CCR%202506-1 CT: - 2020-07-01: 1.85 + 2015-10-01: 1.85 2022-10-01: 2 DE: - 2020-07-01: 2 + 2015-10-01: 2 DC: - 2020-07-01: 2 + 2015-10-01: 2 FL: - 2020-07-01: 2 + 2015-10-01: 2 GA: - 2020-07-01: 1.3 + 2015-10-01: 1.3 GU: - 2020-07-01: 1.65 + 2015-10-01: 1.65 HI: - 2020-07-01: 2 + 2015-10-01: 2 ID: - 2020-07-01: 1.3 + 2015-10-01: 1.3 IL: - 2020-07-01: 1.65 + 2015-10-01: 1.3 + 2016-01-01: 1.65 IN: - 2020-07-01: 1.3 + 2015-10-01: -.inf + 2018-01-01: 1.3 IA: - 2020-07-01: 1.6 + 2015-10-01: 1.6 KS: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. KY: - 2020-07-01: 2 + 2015-10-01: 1.3 + 2020-05-01: 2 LA: - 2020-07-01: 1.3 + 2015-10-01: -.inf + 2020-04-01: 1.3 2022-07-01: 2 ME: - 2020-07-01: 1.85 + 2015-10-01: 1.85 2024-07-01: 2 MD: - 2020-07-01: 2 + 2015-10-01: 2 MA: - 2020-07-01: 2 + 2015-10-01: 2 MI: - 2020-07-01: 2 + 2015-10-01: 2 MN: - 2020-07-01: 1.65 + 2015-10-01: 1.65 2022-09-01: 2 MO: - 2020-07-01: -.inf # Non-BBCE + 2015-10-01: -.inf # Non-BBCE MS: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: 1.3 + 2019-07-01: -.inf # Non-BBCE. MT: - 2020-07-01: 2 + 2015-10-01: 2 NE: - 2020-07-01: 1.3 + 2015-10-01: 1.3 2021-06-01: 1.65 NV: - 2020-07-01: 2 + 2015-10-01: 2 NH: # Only households with at least one dependent child and a specified relative to that child. # (Not currently modeled.) - criteria removed in 2023 - 2020-07-01: 1.85 + 2015-10-01: 1.85 2023-01-01: 2 NJ: - 2020-07-01: 1.85 + 2015-10-01: 1.85 NM: - 2020-07-01: 1.65 + 2015-10-01: 1.65 2024-10-01: 2 # https://www.hca.nm.gov/2024/09/23/governor-expands-snap-access-increases-benefits-for-new-mexicans/ NY: # 150% for those with earned income. # 200% for those with dependent care expenses, not currently entered. - 2020-07-01: 1.5 + 2015-10-01: 1.5 NC: - 2020-07-01: 2 + 2015-10-01: 2 ND: - 2020-07-01: 2 + 2015-10-01: 2 OH: - 2020-07-01: 1.3 + 2015-10-01: 1.3 OK: - 2020-07-01: 1.3 + 2015-10-01: 1.3 OR: - 2020-07-01: 1.85 + 2015-10-01: 1.85 2022-01-01: 2 PA: - 2020-07-01: 1.6 + 2015-10-01: 1.6 # https://www.snapscreener.com/blog/pennsylvania-snap-increase 2022-10-01: 2 RI: - 2020-07-01: 1.85 + 2015-10-01: 1.85 SC: - 2020-07-01: 1.3 + 2015-10-01: 1.3 SD: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. TX: - 2020-07-01: 1.65 + 2015-10-01: 1.65 TN: - 2020-07-01: -.inf # Non-BBCE + 2015-10-01: -.inf # Non-BBCE UT: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. VT: - 2020-07-01: 1.85 + 2015-10-01: 1.85 VI: - 2020-07-01: 1.75 + 2015-10-01: 1.75 VA: - 2020-07-01: 1.3 + 2015-10-01: 1.3 2021-07-01: 2 WA: - 2020-07-01: 2 + 2015-10-01: 2 WV: - 2020-07-01: 2 + 2015-10-01: 1.3 + 2018-07-01: 2 WI: - 2020-07-01: 2 + 2015-10-01: 2 WY: - 2020-07-01: -.inf # Non-BBCE. + 2015-10-01: -.inf # Non-BBCE. metadata: unit: /1 diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml index 78bb7a8404b..8f297856315 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml @@ -1,97 +1,97 @@ description: Whether the SNAP net income limit applies to elderly/disabled households seeking BBCE. # Non-BBCE states skipped. AL: - 2020-07-01: true + 2015-10-01: true AZ: - 2020-07-01: false + 2015-10-01: false CA: - 2020-07-01: true + 2015-10-01: true CO: - 2020-07-01: true + 2015-10-01: true 2024-07-01: false CT: - 2020-07-01: false + 2015-10-01: false DC: - 2020-07-01: false + 2015-10-01: false DE: - 2020-07-01: false + 2015-10-01: false FL: - 2020-07-01: false + 2015-10-01: false GA: - 2020-07-01: false + 2015-10-01: false GU: - 2020-07-01: false + 2015-10-01: false HI: - 2020-07-01: false + 2015-10-01: false 2022-07-01: true IA: - 2020-07-01: false + 2015-10-01: false ID: - 2020-07-01: false + 2015-10-01: false 2024-07-01: true IL: - 2020-07-01: false + 2015-10-01: false IN: - 2020-07-01: true + 2018-01-01: true KY: - 2020-07-01: false + 2015-10-01: false LA: - 2020-07-01: true + 2020-04-01: true MA: - 2020-07-01: false + 2015-10-01: false MD: - 2020-07-01: false + 2015-10-01: false ME: - 2020-07-01: false + 2015-10-01: false MI: - 2020-07-01: false + 2015-10-01: false MN: - 2020-07-01: false + 2015-10-01: false 2024-07-01: true MT: - 2020-07-01: false + 2015-10-01: false NC: - 2020-07-01: false + 2015-10-01: false ND: - 2020-07-01: true + 2015-10-01: true NE: - 2020-07-01: true + 2015-10-01: true NH: - 2020-07-01: false + 2015-10-01: false NJ: - 2020-07-01: false + 2015-10-01: false NM: - 2020-07-01: false + 2015-10-01: false NV: - 2020-07-01: false + 2015-10-01: false NY: - 2020-07-01: false + 2015-10-01: false OH: - 2020-07-01: false + 2015-10-01: false OK: - 2020-07-01: true + 2015-10-01: true OR: - 2020-07-01: false + 2015-10-01: false PA: - 2020-07-01: false + 2015-10-01: false RI: - 2020-07-01: true + 2015-10-01: true SC: - 2020-07-01: false + 2015-10-01: false TX: - 2020-07-01: false + 2015-10-01: false VA: - 2020-07-01: false + 2015-10-01: false VI: - 2020-07-01: false + 2015-10-01: false VT: - 2020-07-01: false + 2015-10-01: false WA: - 2020-07-01: false + 2015-10-01: false WI: - 2020-07-01: false + 2015-10-01: false WV: - 2020-07-01: false + 2015-10-01: false metadata: label: SNAP net income limit for elderly/disabled BBCE households diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml index be083b7dfcc..fc0b0f5fb97 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml @@ -1,95 +1,95 @@ description: Whether the SNAP net income limit applies to non-elderly/disabled households seeking BBCE. # Non-BBCE states skipped. AL: - 2020-07-01: false + 2015-10-01: false AZ: - 2020-07-01: false + 2015-10-01: false CA: - 2020-07-01: true + 2015-10-01: true CO: - 2020-07-01: true + 2015-10-01: true 2024-07-01: false CT: - 2020-07-01: false + 2015-10-01: false DC: - 2020-07-01: false + 2015-10-01: false DE: - 2020-07-01: false + 2015-10-01: false FL: - 2020-07-01: false + 2015-10-01: false GA: - 2020-07-01: false + 2015-10-01: false GU: - 2020-07-01: false + 2015-10-01: false HI: - 2020-07-01: false + 2015-10-01: false 2022-07-01: true IA: - 2020-07-01: false + 2015-10-01: false ID: - 2020-07-01: false + 2015-10-01: false IL: - 2020-07-01: false + 2015-10-01: false IN: - 2020-07-01: true + 2018-01-01: true KY: - 2020-07-01: false + 2015-10-01: false LA: - 2020-07-01: true + 2020-04-01: true MA: - 2020-07-01: false + 2015-10-01: false MD: - 2020-07-01: false + 2015-10-01: false ME: - 2020-07-01: false + 2015-10-01: false MI: - 2020-07-01: false + 2015-10-01: false MN: - 2020-07-01: false + 2015-10-01: false MT: - 2020-07-01: false + 2015-10-01: false NC: - 2020-07-01: false + 2015-10-01: false ND: - 2020-07-01: true + 2015-10-01: true NE: - 2020-07-01: false + 2015-10-01: false NH: - 2020-07-01: false + 2015-10-01: false NJ: - 2020-07-01: false + 2015-10-01: false NM: - 2020-07-01: false + 2015-10-01: false NV: - 2020-07-01: false + 2015-10-01: false NY: - 2020-07-01: false + 2015-10-01: false OH: - 2020-07-01: false + 2015-10-01: false OK: - 2020-07-01: false + 2015-10-01: false OR: - 2020-07-01: false + 2015-10-01: false PA: - 2020-07-01: false + 2015-10-01: false RI: - 2020-07-01: true + 2015-10-01: true SC: - 2020-07-01: false + 2015-10-01: false TX: - 2020-07-01: false + 2015-10-01: false VA: - 2020-07-01: false + 2015-10-01: false VI: - 2020-07-01: false + 2015-10-01: false VT: - 2020-07-01: false + 2015-10-01: false WA: - 2020-07-01: false + 2015-10-01: false WI: - 2020-07-01: false + 2015-10-01: false WV: - 2020-07-01: false + 2015-10-01: false metadata: label: SNAP net income limit for non-elderly/disabled BBCE households diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml index 5e95982789e..615729daa20 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml @@ -1,32 +1,35 @@ description: Requires all members in household to be elderly or disabled for the household to be classified as elderly or disabled for non-cash TANF benefit; otherwise any. # States without BBCE elderly/disabled rules skipped. AL: - 2020-07-01: true + 2015-10-01: true GA: - 2020-07-01: true + 2015-10-01: true ID: - 2020-07-01: false + 2015-10-01: false 2024-07-02: false IL: - 2020-07-01: false + 2015-10-01: false IN: - 2020-07-01: false + 2018-01-01: false LA: - 2020-07-01: false + 2020-04-01: false NE: - 2020-07-01: false + 2015-10-01: false OH: - 2020-07-01: false + 2015-10-01: false OK: - 2020-07-01: false + 2015-10-01: false PA: - 2020-07-01: false + 2015-10-01: false RI: - 2020-07-01: false + 2015-10-01: false SC: - 2020-07-01: true + 2015-10-01: true VI: - 2020-07-01: false + 2015-10-01: false +WV: + 2015-10-01: true + 2018-07-01: false metadata: label: TANF non-cash elderly/disabled household classification requirement From e11fbc97dc4cb5cedeb93d3e2cd14cb2e23bbd9c Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 24 Mar 2026 23:35:08 -0400 Subject: [PATCH 2/7] Fix MS cross-file inconsistency, clean up CO entries, add missing metadata fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add MS to asset_limit (.inf during BBCE), hheod, and non_hheod (false) - Remove redundant CO 2022-11-30 entries, keep regulatory ref as comment - Remove outdated "May have changed" comments for CO - Fix broken asset_limit reference URL (.net CDN → FNS landing page) - Add missing unit/period metadata to hheod, non_hheod, requires_all_for_hheod - Add missing period to asset_limit metadata Co-Authored-By: Claude Opus 4.6 (1M context) --- .../gov/hhs/tanf/non_cash/asset_limit.yaml | 19 ++++++++----------- .../hhs/tanf/non_cash/income_limit/gross.yaml | 8 +------- .../income_limit/net_applies/hheod.yaml | 4 ++++ .../income_limit/net_applies/non_hheod.yaml | 4 ++++ .../tanf/non_cash/requires_all_for_hheod.yaml | 2 ++ 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml index a084ecf2757..8326036fb27 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml @@ -19,13 +19,7 @@ CA: # * 2015-10-01: .inf CO: # * 2015-10-01: .inf - # May have changed prior to 2022-11-30. - 2022-11-30: - value: .inf - metadata: - reference: - - title: Code of Colorado Regulations | Rule Manual Volume 4, SNAP | 4.206(C)(2)(b) (page 35) - href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=10492&fileName=10%20CCR%202506-1 + # https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=10492&fileName=10%20CCR%202506-1 CT: 2015-10-01: .inf DE: @@ -69,7 +63,8 @@ MI: MN: 2015-10-01: .inf MS: - 2015-10-01: -.inf # Non-BBCE. + 2015-10-01: .inf + 2019-07-01: -.inf # Non-BBCE. MT: 2015-10-01: .inf NE: @@ -125,9 +120,11 @@ WY: metadata: label: TANF non-cash asset limit for SNAP BBCE unit: currency-USD + period: year reference: - - title: "USDA: Broad-based Categorical Eligibility" - href: https://fns-prod.azureedge.net/sites/default/files/resource-files/BBCE%20States%20Chart%20(July%202020).pdf - date: 2015-10-01 + - title: USDA Broad-based Categorical Eligibility + href: https://www.fns.usda.gov/snap/broad-based-categorical-eligibility + - title: USDA ERS SNAP Policy Database + href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml index 3f7d844a7ce..bb952cefc81 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml @@ -16,13 +16,7 @@ CA: CO: 2015-10-01: 1.3 2018-06-01: 2 - # Maybe have changed prior to 2022-11-30. - 2022-11-30: - value: 2 - metadata: - reference: - - title: Code of Colorado Regulations | Rule Manual Volume 4, SNAP | 4.206(C)(2)(a)(1) (page 34) - href: https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=10492&fileName=10%20CCR%202506-1 + # https://www.sos.state.co.us/CCR/GenerateRulePdf.do?ruleVersionId=10492&fileName=10%20CCR%202506-1 CT: 2015-10-01: 1.85 2022-10-01: 2 diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml index 8f297856315..c23a9dd01aa 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml @@ -48,6 +48,8 @@ MI: MN: 2015-10-01: false 2024-07-01: true +MS: + 2015-10-01: false MT: 2015-10-01: false NC: @@ -94,6 +96,8 @@ WV: 2015-10-01: false metadata: + unit: bool + period: year label: SNAP net income limit for elderly/disabled BBCE households reference: - title: SNAP Screener SNAP Eligibility Parameters diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml index fc0b0f5fb97..b95e1fe1d56 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml @@ -46,6 +46,8 @@ MI: 2015-10-01: false MN: 2015-10-01: false +MS: + 2015-10-01: false MT: 2015-10-01: false NC: @@ -92,6 +94,8 @@ WV: 2015-10-01: false metadata: + unit: bool + period: year label: SNAP net income limit for non-elderly/disabled BBCE households reference: - title: SNAP Screener SNAP Eligibility Parameters diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml index 615729daa20..d58f5d496ba 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml @@ -32,6 +32,8 @@ WV: 2018-07-01: false metadata: + unit: bool + period: year label: TANF non-cash elderly/disabled household classification requirement reference: - title: SNAP Screener SNAP Eligibility Parameters From 483de12ac54f0ddcb8e74d95b7bdec38aec978c8 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 24 Mar 2026 23:52:09 -0400 Subject: [PATCH 3/7] Fix review findings: VA BBCE dates, trailing whitespace, references, AK entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix VA: was incorrectly marked BBCE from 2015; ERS confirms non-BBCE through Sep 2020, adopted around Jul 2021. Updated all 4 files. - Fix trailing whitespace in requires_all_for_hheod.yaml - Remove redundant ID entry with non-standard date (2024-07-02) - Add ERS SNAP Policy Database reference to 4 files missing it - Fix comment typo in gross.yaml ("asset limit" → "gross income limit") - Add AK asset_limit entry for 2025-07-01 to match gross.yaml Co-Authored-By: Claude Opus 4.6 (1M context) --- .../parameters/gov/hhs/tanf/non_cash/asset_limit.yaml | 4 +++- .../gov/hhs/tanf/non_cash/income_limit/gross.yaml | 6 ++++-- .../hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml | 4 +++- .../tanf/non_cash/income_limit/net_applies/non_hheod.yaml | 4 +++- .../gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml | 5 +++-- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml index 8326036fb27..1c7edc497cc 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml @@ -11,6 +11,7 @@ AK: # Alaska is not in the list, indicating it has not elected to BBCE. # Non-BBCE states are entered as negative infinity asset limit. 2015-10-01: -.inf + 2025-07-01: .inf AZ: 2015-10-01: .inf AR: @@ -107,7 +108,8 @@ VT: VI: # * 2015-10-01: .inf VA: - 2015-10-01: .inf + 2015-10-01: -.inf # Non-BBCE per ERS data through Sep 2020. + 2021-07-01: .inf WA: 2015-10-01: .inf WV: # * diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml index bb952cefc81..8011a50b24d 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml @@ -4,7 +4,7 @@ AL: 2015-10-01: 1.3 AK: # Alaska is not in the list, indicating it has not elected to BBCE. - # Non-BBCE states are entered as negative .infinity asset limit. + # Non-BBCE states are entered as negative .infinity gross income limit. 2015-10-01: -.inf 2025-07-01: 2 AZ: @@ -122,7 +122,7 @@ VT: VI: 2015-10-01: 1.75 VA: - 2015-10-01: 1.3 + 2015-10-01: -.inf # Non-BBCE per ERS data through Sep 2020. 2021-07-01: 2 WA: 2015-10-01: 2 @@ -141,5 +141,7 @@ metadata: reference: - title: USDA Broad-Based Categorical Eligibility (BBCE) href: https://www.fns.usda.gov/snap/broad-based-categorical-eligibility + - title: USDA ERS SNAP Policy Database + href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml index c23a9dd01aa..b49941bf99b 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml @@ -83,7 +83,7 @@ SC: TX: 2015-10-01: false VA: - 2015-10-01: false + 2021-07-01: false VI: 2015-10-01: false VT: @@ -102,5 +102,7 @@ metadata: reference: - title: SNAP Screener SNAP Eligibility Parameters href: https://www.snapscreener.com/?p=table + - title: USDA ERS SNAP Policy Database + href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml index b95e1fe1d56..0387ffcbfa4 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml @@ -81,7 +81,7 @@ SC: TX: 2015-10-01: false VA: - 2015-10-01: false + 2021-07-01: false VI: 2015-10-01: false VT: @@ -100,5 +100,7 @@ metadata: reference: - title: SNAP Screener SNAP Eligibility Parameters href: https://www.snapscreener.com/?p=table + - title: USDA ERS SNAP Policy Database + href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml index d58f5d496ba..24023835b66 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml @@ -5,8 +5,7 @@ AL: GA: 2015-10-01: true ID: - 2015-10-01: false - 2024-07-02: false + 2015-10-01: false IL: 2015-10-01: false IN: @@ -38,5 +37,7 @@ metadata: reference: - title: SNAP Screener SNAP Eligibility Parameters href: https://www.snapscreener.com/?p=table + - title: USDA ERS SNAP Policy Database + href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code From b1ac1d17d9503a9d3401fc07b64fa6227c6f5c90 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Wed, 25 Mar 2026 13:17:44 -0400 Subject: [PATCH 4/7] Fix MT income tax applying preferential LTCG rates when net CG is negative Two bugs fixed: 1. mt_regular_income_tax: only subtract LTCG from taxable income when net capital gains (STCG + LTCG) are positive. When STCG losses exceed LTCG, all income should be taxed at ordinary rates. 2. mt_capital_gains_tax_joint: convert from Person to TaxUnit entity to fix mismatch where LTCG and taxable income are on different persons in joint filing, causing both to get $0 CG tax. Closes #7859 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../fix-mt-ltcg-negative-net-gains.fixed.md | 1 + ..._gains_tax_applicable_threshold_joint.yaml | 8 ++-- .../gov/states/mt/tax/income/integration.yaml | 37 ++++++++++++++++++- ...al_gains_tax_applicable_threshold_joint.py | 13 +++---- .../mt_capital_gains_tax_joint.py | 21 ++++++----- .../mt_regular_income_tax_indiv.py | 13 ++++--- .../mt_regular_income_tax_joint.py | 13 ++++--- 7 files changed, 74 insertions(+), 32 deletions(-) create mode 100644 changelog.d/fix-mt-ltcg-negative-net-gains.fixed.md diff --git a/changelog.d/fix-mt-ltcg-negative-net-gains.fixed.md b/changelog.d/fix-mt-ltcg-negative-net-gains.fixed.md new file mode 100644 index 00000000000..865137b3167 --- /dev/null +++ b/changelog.d/fix-mt-ltcg-negative-net-gains.fixed.md @@ -0,0 +1 @@ +Fix Montana income tax incorrectly applying preferential LTCG rates when net capital gains are negative. diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.yaml index 92e541f80fe..abafe89783b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.yaml @@ -2,7 +2,7 @@ period: 2024 input: long_term_capital_gains: 2_000 - mt_taxable_income_indiv: 2_500 + mt_taxable_income_joint: 2_500 filing_status: SINGLE state_code: MT output: @@ -12,7 +12,7 @@ period: 2024 input: long_term_capital_gains: 2_000 - mt_taxable_income_indiv: 1_000 + mt_taxable_income_joint: 1_000 filing_status: SINGLE state_code: MT output: @@ -22,7 +22,7 @@ period: 2024 input: long_term_capital_gains: 2_000 - mt_taxable_income_indiv: 40_000 + mt_taxable_income_joint: 40_000 filing_status: HEAD_OF_HOUSEHOLD state_code: MT output: @@ -32,7 +32,7 @@ period: 2024 input: long_term_capital_gains: 2_000 - mt_taxable_income_indiv: 40_000 + mt_taxable_income_joint: 40_000 filing_status: JOINT state_code: MT output: diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml index 35120e6918f..653cbcec230 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml @@ -291,7 +291,7 @@ members: [person1, person2] state_fips: 30 output: - mt_capital_gains_tax_joint: [59, 0] + mt_capital_gains_tax_joint: 59 mt_income_tax: 59 - name: 2251-MT.yaml @@ -371,3 +371,38 @@ output: adjusted_gross_income: -3_000 mt_income_tax: 0 + +- name: MT joint 2024 - net capital loss should use ordinary rates (issue 7859) + period: 2024 + absolute_error_margin: 1 + input: + people: + primary: + age: 56 + self_employment_income: -188_899.73 + taxable_interest_income: 23_241.32 + short_term_capital_gains: -1_575_884.50 + long_term_capital_gains: 532_687.31 + taxable_private_pension_income: 21_262.69 + spouse: + age: 58 + employment_income: 538_104.25 + tax_units: + tax_unit: + members: [primary, spouse] + families: + family: + members: [primary, spouse] + spm_units: + spm_unit: + members: [primary, spouse] + households: + household: + members: [primary, spouse] + state_fips: 30 + marital_units: + marital_unit: + members: [primary, spouse] + output: + mt_taxable_income: 361_508 + mt_income_tax: 20_837 diff --git a/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.py b/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.py index 01e082ae59e..82d08ecf88e 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.py @@ -3,21 +3,18 @@ class mt_capital_gains_tax_applicable_threshold_joint(Variable): value_type = float - entity = Person + entity = TaxUnit label = "Montana applicable threshold for the capital gains tax when married couples file jointly" unit = USD definition_period = YEAR reference = "https://mtrevenue.gov/wp-content/uploads/dlm_uploads/2023/12/Form_2_2023_Instructions.pdf#page=6" # Net Long-Term Capital Gains Tax Table defined_for = StateCode.MT - def formula(person, period, parameters): + def formula(tax_unit, period, parameters): p = parameters(period).gov.states.mt.tax.income.main.capital_gains - capital_gains = person("long_term_capital_gains", period) - taxable_income = person("mt_taxable_income_indiv", period) - filing_status = person.tax_unit( - "filing_status", - period, - ) + capital_gains = add(tax_unit, period, ["long_term_capital_gains"]) + taxable_income = add(tax_unit, period, ["mt_taxable_income_joint"]) + filing_status = tax_unit("filing_status", period) status = filing_status.possible_values non_qualified_income = max_(taxable_income - capital_gains, 0) rate_threshold = select( diff --git a/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.py b/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.py index a7e7b9960c6..03825b22fc4 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.py @@ -3,33 +3,36 @@ class mt_capital_gains_tax_joint(Variable): value_type = float - entity = Person + entity = TaxUnit label = "Montana net long-term capital gains tax when married couples file jointly" unit = USD definition_period = YEAR reference = "https://mtrevenue.gov/wp-content/uploads/dlm_uploads/2023/12/Form_2_2023_Instructions.pdf#page=6" # Net Long-Term Capital Gains Tax Table defined_for = StateCode.MT - def formula(person, period, parameters): + def formula(tax_unit, period, parameters): p = parameters(period).gov.states.mt.tax.income.main.capital_gains # the tax for capital gains comes into effect after 2024 if p.in_effect: # Line instructions from the 2024 Montana Individual Income Tax Return Form 2 # https://revenue.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2/2024_Montana_Individual_Income_Tax_Return_Form_2.pdf#page=2 - # Line 1 - taxable_income = person("mt_taxable_income_joint", period) - # Line 2 - capital_gains = person("long_term_capital_gains", period) - # No tax on zero or negative capital gains + # Line 1 — aggregate across persons for joint filing + taxable_income = add(tax_unit, period, ["mt_taxable_income_joint"]) + # Line 2 — aggregate LTCG across persons + capital_gains = add(tax_unit, period, ["long_term_capital_gains"]) + stcg = add(tax_unit, period, ["short_term_capital_gains"]) + net_cg = capital_gains + stcg + # No preferential CG rates when net capital gains are negative + capital_gains = where(net_cg > 0, max_(capital_gains, 0), 0) # Line 3 lesser_of_cg_and_taxable_income = min_(capital_gains, taxable_income) # Line 4 excess_over_taxable_income = max_( taxable_income - lesser_of_cg_and_taxable_income, 0 ) - filing_status = person.tax_unit("filing_status", period) + filing_status = tax_unit("filing_status", period) # Line 5 - applicable_threshold = person( + applicable_threshold = tax_unit( "mt_capital_gains_tax_applicable_threshold_joint", period ) status = filing_status.possible_values diff --git a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_indiv.py b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_indiv.py index 04e8aeb3e74..c0de9b11af9 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_indiv.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_indiv.py @@ -17,11 +17,14 @@ def formula(person, period, parameters): period, ) if p.capital_gains.in_effect: - capital_gains = person("long_term_capital_gains", period) - # Only subtract positive capital gains (they're taxed separately) - # Negative capital gains should not create phantom taxable income - capital_gains = max_(capital_gains, 0) - taxable_income = max_(taxable_income - capital_gains, 0) + ltcg = person("long_term_capital_gains", period) + stcg = person("short_term_capital_gains", period) + net_cg = ltcg + stcg + # Only subtract LTCG when net capital gains are positive. + # When STCG losses exceed LTCG, the net position is a loss + # and all income should be taxed at ordinary rates. + cg_to_subtract = where(net_cg > 0, max_(ltcg, 0), 0) + taxable_income = max_(taxable_income - cg_to_subtract, 0) status = filing_status.possible_values return select( [ diff --git a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_joint.py b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_joint.py index bb5c73f8aef..805e0622ac0 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_joint.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_joint.py @@ -16,11 +16,14 @@ def formula(tax_unit, period, parameters): status = filing_status.possible_values if p.capital_gains.in_effect: - capital_gains = add(tax_unit, period, ["long_term_capital_gains"]) - # Only subtract positive capital gains (they're taxed separately) - # Negative capital gains should not create phantom taxable income - capital_gains = max_(capital_gains, 0) - taxable_income = max_(taxable_income - capital_gains, 0) + ltcg = add(tax_unit, period, ["long_term_capital_gains"]) + stcg = add(tax_unit, period, ["short_term_capital_gains"]) + net_cg = ltcg + stcg + # Only subtract LTCG when net capital gains are positive. + # When STCG losses exceed LTCG, the net position is a loss + # and all income should be taxed at ordinary rates. + cg_to_subtract = where(net_cg > 0, max_(ltcg, 0), 0) + taxable_income = max_(taxable_income - cg_to_subtract, 0) return select( [ filing_status == status.SINGLE, From 741ec86f342ca358afe37a8e8b0ebb4a8cb7f80a Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Wed, 25 Mar 2026 13:32:53 -0400 Subject: [PATCH 5/7] Add edge case tests for MT capital gains tax fix Integration tests: - Positive net CG with STCG losses: confirms preferential rates still apply - LTCG on head, wages on spouse: confirms TaxUnit aggregation works cross-person - Both spouses with LTCG: confirms combined LTCG aggregation Unit tests: - Negative net CG: confirms $0 CG tax when STCG losses exceed LTCG - Positive net CG with STCG losses: confirms full LTCG gets preferential rates Co-Authored-By: Claude Opus 4.6 (1M context) --- .../mt_capital_gains_tax_joint.yaml | 29 +++++ .../gov/states/mt/tax/income/integration.yaml | 105 ++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.yaml index eb8c13ac7b9..c36f6f2771a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.yaml @@ -75,3 +75,32 @@ state_code: MT output: mt_capital_gains_tax_joint: 410 + +- name: No CG tax when net capital gains are negative + # LTCG=50K but STCG=-80K → net=-30K → no preferential treatment + period: 2024 + input: + long_term_capital_gains: 50_000 + short_term_capital_gains: -80_000 + mt_taxable_income_joint: 100_000 + filing_status: JOINT + state_code: MT + output: + mt_capital_gains_tax_joint: 0 + +- name: CG tax applies when net capital gains are positive despite STCG losses + # LTCG=50K, STCG=-20K → net=+30K → preferential rates apply to full LTCG + period: 2024 + input: + long_term_capital_gains: 50_000 + short_term_capital_gains: -20_000 + mt_taxable_income_joint: 100_000 + mt_capital_gains_tax_applicable_threshold_joint: 41_000 + filing_status: JOINT + state_code: MT + output: + # lesser_of_cg_and_taxable = min(50K, 100K) = 50K + # excess_over_taxable = max(100K - 50K, 0) = 50K + # excess_over_threshold = max(41K - 50K, 0) = 0 + # All 50K taxed at higher rate: 50K * 0.041 = 2050 + mt_capital_gains_tax_joint: 2_050 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml index 653cbcec230..e0f0f23ad3b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml @@ -406,3 +406,108 @@ output: mt_taxable_income: 361_508 mt_income_tax: 20_837 + +- name: MT joint 2024 - positive net CG with STCG losses applies preferential rates + # LTCG=50K, STCG=-30K, net=+20K → preferential CG rates should apply + period: 2024 + absolute_error_margin: 1 + input: + people: + head: + age: 40 + long_term_capital_gains: 50_000 + short_term_capital_gains: -30_000 + spouse: + age: 40 + employment_income: 100_000 + tax_units: + tax_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_fips: 30 + marital_units: + marital_unit: + members: [head, spouse] + output: + mt_taxable_income: 90_800 + mt_regular_income_tax_joint: 1_918 + mt_capital_gains_tax_joint: 2_050 + mt_income_tax: 3_968 + +- name: MT joint 2024 - LTCG on head, wages on spouse (cross-person aggregation) + # Tests TaxUnit-level CG tax aggregation across spouses + period: 2024 + absolute_error_margin: 1 + input: + people: + head: + age: 40 + long_term_capital_gains: 30_000 + spouse: + age: 40 + employment_income: 80_000 + tax_units: + tax_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_fips: 30 + marital_units: + marital_unit: + members: [head, spouse] + output: + mt_taxable_income: 80_800 + mt_regular_income_tax_joint: 2_505 + mt_capital_gains_tax_joint: 1_230 + mt_income_tax: 3_735 + +- name: MT joint 2024 - both spouses have LTCG + # Tests combined LTCG aggregation at TaxUnit level + period: 2024 + absolute_error_margin: 1 + input: + people: + head: + age: 40 + long_term_capital_gains: 15_000 + employment_income: 40_000 + spouse: + age: 40 + long_term_capital_gains: 10_000 + employment_income: 30_000 + tax_units: + tax_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_fips: 30 + marital_units: + marital_unit: + members: [head, spouse] + output: + mt_taxable_income: 65_800 + mt_regular_income_tax_joint: 1_918 + mt_capital_gains_tax_joint: 1_025 + mt_income_tax: 2_943 From 74f530d6b4120ff32db2929efa462d08416d1765 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Wed, 25 Mar 2026 13:51:54 -0400 Subject: [PATCH 6/7] Add BBCE regression tests and improve reference specificity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 4 gross income tests: CO BBCE at 200%, CO above limit, MS non-BBCE, IN BBCE at 130% — covering the CO and MS bug fixes - Add 3 asset tests: MS non-BBCE fails, IN below/above $5K limit - Improve ERS reference titles with specific column names across all 5 files Co-Authored-By: Claude Opus 4.6 (1M context) --- .../gov/hhs/tanf/non_cash/asset_limit.yaml | 2 +- .../hhs/tanf/non_cash/income_limit/gross.yaml | 2 +- .../income_limit/net_applies/hheod.yaml | 2 +- .../income_limit/net_applies/non_hheod.yaml | 2 +- .../tanf/non_cash/requires_all_for_hheod.yaml | 2 +- .../meets_tanf_non_cash_asset_test.yaml | 24 ++++++++++++++ ...meets_tanf_non_cash_gross_income_test.yaml | 32 +++++++++++++++++++ 7 files changed, 61 insertions(+), 5 deletions(-) diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml index 1c7edc497cc..92a6c4439f3 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/asset_limit.yaml @@ -126,7 +126,7 @@ metadata: reference: - title: USDA Broad-based Categorical Eligibility href: https://www.fns.usda.gov/snap/broad-based-categorical-eligibility - - title: USDA ERS SNAP Policy Database + - title: USDA ERS SNAP Policy Database (bbce_asset/bbce_a_amt columns, Jan 1996 - Dec 2020) href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml index 8011a50b24d..68c9f0dcc77 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/gross.yaml @@ -141,7 +141,7 @@ metadata: reference: - title: USDA Broad-Based Categorical Eligibility (BBCE) href: https://www.fns.usda.gov/snap/broad-based-categorical-eligibility - - title: USDA ERS SNAP Policy Database + - title: USDA ERS SNAP Policy Database (bbce_inclmt column, Jan 1996 - Dec 2020) href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml index b49941bf99b..85355ff472d 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/hheod.yaml @@ -102,7 +102,7 @@ metadata: reference: - title: SNAP Screener SNAP Eligibility Parameters href: https://www.snapscreener.com/?p=table - - title: USDA ERS SNAP Policy Database + - title: USDA ERS SNAP Policy Database (bbce_elddisinclmt column, Jan 1996 - Dec 2020) href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml index 0387ffcbfa4..bd3e7e648b3 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/income_limit/net_applies/non_hheod.yaml @@ -100,7 +100,7 @@ metadata: reference: - title: SNAP Screener SNAP Eligibility Parameters href: https://www.snapscreener.com/?p=table - - title: USDA ERS SNAP Policy Database + - title: USDA ERS SNAP Policy Database (bbce_inclmt column, Jan 1996 - Dec 2020) href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code diff --git a/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml b/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml index 24023835b66..9fd237e98f8 100644 --- a/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml +++ b/policyengine_us/parameters/gov/hhs/tanf/non_cash/requires_all_for_hheod.yaml @@ -37,7 +37,7 @@ metadata: reference: - title: SNAP Screener SNAP Eligibility Parameters href: https://www.snapscreener.com/?p=table - - title: USDA ERS SNAP Policy Database + - title: USDA ERS SNAP Policy Database (bbce_multiple column, Jan 1996 - Dec 2020) href: https://www.ers.usda.gov/data-products/snap-policy-data-sets breakdown: - state_code diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_asset_test.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_asset_test.yaml index 1bb772cfc96..1ca2b5f96ea 100644 --- a/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_asset_test.yaml +++ b/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_asset_test.yaml @@ -18,3 +18,27 @@ snap_assets: 61_200 # $5,100 monthly output: meets_tanf_non_cash_asset_test: false + +- name: MS asset test fails (non-BBCE state, bug fix verification) + period: 2024 + input: + state_code_str: MS + snap_assets: 0 + output: + meets_tanf_non_cash_asset_test: false + +- name: IN asset below $5K limit passes + period: 2024 + input: + state_code_str: IN + snap_assets: 48_000 # $4,000 monthly + output: + meets_tanf_non_cash_asset_test: true + +- name: IN asset above $5K limit fails + period: 2024 + input: + state_code_str: IN + snap_assets: 72_000 # $6,000 monthly + output: + meets_tanf_non_cash_asset_test: false diff --git a/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_gross_income_test.yaml b/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_gross_income_test.yaml index 12a7e73bcb8..8ab3c10abd2 100644 --- a/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_gross_income_test.yaml +++ b/policyengine_us/tests/policy/baseline/gov/hhs/tanf/non_cash/meets_tanf_non_cash_gross_income_test.yaml @@ -21,3 +21,35 @@ snap_gross_income_fpg_ratio: 1.95 # Gross income limit is 200% of FPL, so this should be true. meets_tanf_non_cash_gross_income_test: true + +- name: CO has BBCE at 200% FPL (bug fix verification) + period: 2024-01 + input: + snap_gross_income_fpg_ratio: 1.95 + state_code: CO + output: + meets_tanf_non_cash_gross_income_test: true + +- name: CO rejects income above 200% FPL + period: 2024-01 + input: + snap_gross_income_fpg_ratio: 2.05 + state_code: CO + output: + meets_tanf_non_cash_gross_income_test: false + +- name: MS has no BBCE (non-BBCE state, bug fix verification) + period: 2024-01 + input: + snap_gross_income_fpg_ratio: 0.5 + state_code: MS + output: + meets_tanf_non_cash_gross_income_test: false + +- name: IN has BBCE at 130% FPL + period: 2024-01 + input: + snap_gross_income_fpg_ratio: 1.25 + state_code: IN + output: + meets_tanf_non_cash_gross_income_test: true From 941b1421ece1c403361120fe28af7abdb45563c7 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Wed, 25 Mar 2026 14:46:03 -0400 Subject: [PATCH 7/7] Remove Montana capital gains changes from SNAP BBCE PR These MT changes were accidentally merged into this branch and belong in a separate PR. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../fix-mt-ltcg-negative-net-gains.fixed.md | 1 - ..._gains_tax_applicable_threshold_joint.yaml | 8 +- .../mt_capital_gains_tax_joint.yaml | 29 ---- .../gov/states/mt/tax/income/integration.yaml | 142 +----------------- ...al_gains_tax_applicable_threshold_joint.py | 13 +- .../mt_capital_gains_tax_joint.py | 21 ++- .../mt_regular_income_tax_indiv.py | 13 +- .../mt_regular_income_tax_joint.py | 13 +- 8 files changed, 32 insertions(+), 208 deletions(-) delete mode 100644 changelog.d/fix-mt-ltcg-negative-net-gains.fixed.md diff --git a/changelog.d/fix-mt-ltcg-negative-net-gains.fixed.md b/changelog.d/fix-mt-ltcg-negative-net-gains.fixed.md deleted file mode 100644 index 865137b3167..00000000000 --- a/changelog.d/fix-mt-ltcg-negative-net-gains.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix Montana income tax incorrectly applying preferential LTCG rates when net capital gains are negative. diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.yaml index abafe89783b..92e541f80fe 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.yaml @@ -2,7 +2,7 @@ period: 2024 input: long_term_capital_gains: 2_000 - mt_taxable_income_joint: 2_500 + mt_taxable_income_indiv: 2_500 filing_status: SINGLE state_code: MT output: @@ -12,7 +12,7 @@ period: 2024 input: long_term_capital_gains: 2_000 - mt_taxable_income_joint: 1_000 + mt_taxable_income_indiv: 1_000 filing_status: SINGLE state_code: MT output: @@ -22,7 +22,7 @@ period: 2024 input: long_term_capital_gains: 2_000 - mt_taxable_income_joint: 40_000 + mt_taxable_income_indiv: 40_000 filing_status: HEAD_OF_HOUSEHOLD state_code: MT output: @@ -32,7 +32,7 @@ period: 2024 input: long_term_capital_gains: 2_000 - mt_taxable_income_joint: 40_000 + mt_taxable_income_indiv: 40_000 filing_status: JOINT state_code: MT output: diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.yaml index c36f6f2771a..eb8c13ac7b9 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.yaml @@ -75,32 +75,3 @@ state_code: MT output: mt_capital_gains_tax_joint: 410 - -- name: No CG tax when net capital gains are negative - # LTCG=50K but STCG=-80K → net=-30K → no preferential treatment - period: 2024 - input: - long_term_capital_gains: 50_000 - short_term_capital_gains: -80_000 - mt_taxable_income_joint: 100_000 - filing_status: JOINT - state_code: MT - output: - mt_capital_gains_tax_joint: 0 - -- name: CG tax applies when net capital gains are positive despite STCG losses - # LTCG=50K, STCG=-20K → net=+30K → preferential rates apply to full LTCG - period: 2024 - input: - long_term_capital_gains: 50_000 - short_term_capital_gains: -20_000 - mt_taxable_income_joint: 100_000 - mt_capital_gains_tax_applicable_threshold_joint: 41_000 - filing_status: JOINT - state_code: MT - output: - # lesser_of_cg_and_taxable = min(50K, 100K) = 50K - # excess_over_taxable = max(100K - 50K, 0) = 50K - # excess_over_threshold = max(41K - 50K, 0) = 0 - # All 50K taxed at higher rate: 50K * 0.041 = 2050 - mt_capital_gains_tax_joint: 2_050 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml index e0f0f23ad3b..35120e6918f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/tax/income/integration.yaml @@ -291,7 +291,7 @@ members: [person1, person2] state_fips: 30 output: - mt_capital_gains_tax_joint: 59 + mt_capital_gains_tax_joint: [59, 0] mt_income_tax: 59 - name: 2251-MT.yaml @@ -371,143 +371,3 @@ output: adjusted_gross_income: -3_000 mt_income_tax: 0 - -- name: MT joint 2024 - net capital loss should use ordinary rates (issue 7859) - period: 2024 - absolute_error_margin: 1 - input: - people: - primary: - age: 56 - self_employment_income: -188_899.73 - taxable_interest_income: 23_241.32 - short_term_capital_gains: -1_575_884.50 - long_term_capital_gains: 532_687.31 - taxable_private_pension_income: 21_262.69 - spouse: - age: 58 - employment_income: 538_104.25 - tax_units: - tax_unit: - members: [primary, spouse] - families: - family: - members: [primary, spouse] - spm_units: - spm_unit: - members: [primary, spouse] - households: - household: - members: [primary, spouse] - state_fips: 30 - marital_units: - marital_unit: - members: [primary, spouse] - output: - mt_taxable_income: 361_508 - mt_income_tax: 20_837 - -- name: MT joint 2024 - positive net CG with STCG losses applies preferential rates - # LTCG=50K, STCG=-30K, net=+20K → preferential CG rates should apply - period: 2024 - absolute_error_margin: 1 - input: - people: - head: - age: 40 - long_term_capital_gains: 50_000 - short_term_capital_gains: -30_000 - spouse: - age: 40 - employment_income: 100_000 - tax_units: - tax_unit: - members: [head, spouse] - families: - family: - members: [head, spouse] - spm_units: - spm_unit: - members: [head, spouse] - households: - household: - members: [head, spouse] - state_fips: 30 - marital_units: - marital_unit: - members: [head, spouse] - output: - mt_taxable_income: 90_800 - mt_regular_income_tax_joint: 1_918 - mt_capital_gains_tax_joint: 2_050 - mt_income_tax: 3_968 - -- name: MT joint 2024 - LTCG on head, wages on spouse (cross-person aggregation) - # Tests TaxUnit-level CG tax aggregation across spouses - period: 2024 - absolute_error_margin: 1 - input: - people: - head: - age: 40 - long_term_capital_gains: 30_000 - spouse: - age: 40 - employment_income: 80_000 - tax_units: - tax_unit: - members: [head, spouse] - families: - family: - members: [head, spouse] - spm_units: - spm_unit: - members: [head, spouse] - households: - household: - members: [head, spouse] - state_fips: 30 - marital_units: - marital_unit: - members: [head, spouse] - output: - mt_taxable_income: 80_800 - mt_regular_income_tax_joint: 2_505 - mt_capital_gains_tax_joint: 1_230 - mt_income_tax: 3_735 - -- name: MT joint 2024 - both spouses have LTCG - # Tests combined LTCG aggregation at TaxUnit level - period: 2024 - absolute_error_margin: 1 - input: - people: - head: - age: 40 - long_term_capital_gains: 15_000 - employment_income: 40_000 - spouse: - age: 40 - long_term_capital_gains: 10_000 - employment_income: 30_000 - tax_units: - tax_unit: - members: [head, spouse] - families: - family: - members: [head, spouse] - spm_units: - spm_unit: - members: [head, spouse] - households: - household: - members: [head, spouse] - state_fips: 30 - marital_units: - marital_unit: - members: [head, spouse] - output: - mt_taxable_income: 65_800 - mt_regular_income_tax_joint: 1_918 - mt_capital_gains_tax_joint: 1_025 - mt_income_tax: 2_943 diff --git a/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.py b/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.py index 82d08ecf88e..01e082ae59e 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_applicable_threshold_joint.py @@ -3,18 +3,21 @@ class mt_capital_gains_tax_applicable_threshold_joint(Variable): value_type = float - entity = TaxUnit + entity = Person label = "Montana applicable threshold for the capital gains tax when married couples file jointly" unit = USD definition_period = YEAR reference = "https://mtrevenue.gov/wp-content/uploads/dlm_uploads/2023/12/Form_2_2023_Instructions.pdf#page=6" # Net Long-Term Capital Gains Tax Table defined_for = StateCode.MT - def formula(tax_unit, period, parameters): + def formula(person, period, parameters): p = parameters(period).gov.states.mt.tax.income.main.capital_gains - capital_gains = add(tax_unit, period, ["long_term_capital_gains"]) - taxable_income = add(tax_unit, period, ["mt_taxable_income_joint"]) - filing_status = tax_unit("filing_status", period) + capital_gains = person("long_term_capital_gains", period) + taxable_income = person("mt_taxable_income_indiv", period) + filing_status = person.tax_unit( + "filing_status", + period, + ) status = filing_status.possible_values non_qualified_income = max_(taxable_income - capital_gains, 0) rate_threshold = select( diff --git a/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.py b/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.py index 03825b22fc4..a7e7b9960c6 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/capital_gains/mt_capital_gains_tax_joint.py @@ -3,36 +3,33 @@ class mt_capital_gains_tax_joint(Variable): value_type = float - entity = TaxUnit + entity = Person label = "Montana net long-term capital gains tax when married couples file jointly" unit = USD definition_period = YEAR reference = "https://mtrevenue.gov/wp-content/uploads/dlm_uploads/2023/12/Form_2_2023_Instructions.pdf#page=6" # Net Long-Term Capital Gains Tax Table defined_for = StateCode.MT - def formula(tax_unit, period, parameters): + def formula(person, period, parameters): p = parameters(period).gov.states.mt.tax.income.main.capital_gains # the tax for capital gains comes into effect after 2024 if p.in_effect: # Line instructions from the 2024 Montana Individual Income Tax Return Form 2 # https://revenue.mt.gov/files/Forms/Montana-Individual-Income-Tax-Return-Form-2/2024_Montana_Individual_Income_Tax_Return_Form_2.pdf#page=2 - # Line 1 — aggregate across persons for joint filing - taxable_income = add(tax_unit, period, ["mt_taxable_income_joint"]) - # Line 2 — aggregate LTCG across persons - capital_gains = add(tax_unit, period, ["long_term_capital_gains"]) - stcg = add(tax_unit, period, ["short_term_capital_gains"]) - net_cg = capital_gains + stcg - # No preferential CG rates when net capital gains are negative - capital_gains = where(net_cg > 0, max_(capital_gains, 0), 0) + # Line 1 + taxable_income = person("mt_taxable_income_joint", period) + # Line 2 + capital_gains = person("long_term_capital_gains", period) + # No tax on zero or negative capital gains # Line 3 lesser_of_cg_and_taxable_income = min_(capital_gains, taxable_income) # Line 4 excess_over_taxable_income = max_( taxable_income - lesser_of_cg_and_taxable_income, 0 ) - filing_status = tax_unit("filing_status", period) + filing_status = person.tax_unit("filing_status", period) # Line 5 - applicable_threshold = tax_unit( + applicable_threshold = person( "mt_capital_gains_tax_applicable_threshold_joint", period ) status = filing_status.possible_values diff --git a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_indiv.py b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_indiv.py index c0de9b11af9..04e8aeb3e74 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_indiv.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_indiv.py @@ -17,14 +17,11 @@ def formula(person, period, parameters): period, ) if p.capital_gains.in_effect: - ltcg = person("long_term_capital_gains", period) - stcg = person("short_term_capital_gains", period) - net_cg = ltcg + stcg - # Only subtract LTCG when net capital gains are positive. - # When STCG losses exceed LTCG, the net position is a loss - # and all income should be taxed at ordinary rates. - cg_to_subtract = where(net_cg > 0, max_(ltcg, 0), 0) - taxable_income = max_(taxable_income - cg_to_subtract, 0) + capital_gains = person("long_term_capital_gains", period) + # Only subtract positive capital gains (they're taxed separately) + # Negative capital gains should not create phantom taxable income + capital_gains = max_(capital_gains, 0) + taxable_income = max_(taxable_income - capital_gains, 0) status = filing_status.possible_values return select( [ diff --git a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_joint.py b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_joint.py index 805e0622ac0..bb5c73f8aef 100644 --- a/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_joint.py +++ b/policyengine_us/variables/gov/states/mt/tax/income/tax_calculation/mt_regular_income_tax_joint.py @@ -16,14 +16,11 @@ def formula(tax_unit, period, parameters): status = filing_status.possible_values if p.capital_gains.in_effect: - ltcg = add(tax_unit, period, ["long_term_capital_gains"]) - stcg = add(tax_unit, period, ["short_term_capital_gains"]) - net_cg = ltcg + stcg - # Only subtract LTCG when net capital gains are positive. - # When STCG losses exceed LTCG, the net position is a loss - # and all income should be taxed at ordinary rates. - cg_to_subtract = where(net_cg > 0, max_(ltcg, 0), 0) - taxable_income = max_(taxable_income - cg_to_subtract, 0) + capital_gains = add(tax_unit, period, ["long_term_capital_gains"]) + # Only subtract positive capital gains (they're taxed separately) + # Negative capital gains should not create phantom taxable income + capital_gains = max_(capital_gains, 0) + taxable_income = max_(taxable_income - capital_gains, 0) return select( [ filing_status == status.SINGLE,