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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/fix-mt-ltcg-negative-net-gains.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix Montana income tax incorrectly applying preferential LTCG rates when net capital gains are negative.
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,25 @@
state_code: MT
output:
mt_capital_gains_tax_applicable_threshold_indiv: 0

- name: Indiv threshold uses net LTCG after STCG losses when MFSS is enabled
period: 2024
reforms: policyengine_us.tests.policy.baseline.gov.states.mt.tax.income.enable_mt_mfss_2024_reform.enable_mt_mfss_2024_reform
input:
people:
person1:
mt_taxable_income_indiv: 40_000
long_term_capital_gains: 50_000
short_term_capital_gains: -20_000
person2:
mt_taxable_income_indiv: 0
tax_units:
tax_unit:
members: [person1, person2]
state_filing_status_if_married_filing_separately_on_same_return: SEPARATE
households:
household:
members: [person1, person2]
state_code: MT
output:
mt_capital_gains_tax_applicable_threshold_indiv: [10_500, 20_500]
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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:
Expand All @@ -32,8 +32,19 @@
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:
mt_capital_gains_tax_applicable_threshold_joint: 3_000

- name: Short-term losses reduce the remaining joint CG threshold
period: 2024
input:
long_term_capital_gains: 50_000
short_term_capital_gains: -20_000
mt_taxable_income_joint: 40_000
filing_status: JOINT
state_code: MT
output:
mt_capital_gains_tax_applicable_threshold_joint: 31_000
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,27 @@
state_code: MT
output:
mt_capital_gains_tax_indiv: 0

- name: Indiv CG tax uses net LTCG after STCG losses when MFSS is enabled
period: 2024
reforms: policyengine_us.tests.policy.baseline.gov.states.mt.tax.income.enable_mt_mfss_2024_reform.enable_mt_mfss_2024_reform
input:
people:
person1:
long_term_capital_gains: 50_000
short_term_capital_gains: -20_000
mt_capital_gains_tax_applicable_threshold_indiv: 10_000
person2:
long_term_capital_gains: 0
short_term_capital_gains: 0
mt_capital_gains_tax_applicable_threshold_indiv: 20_500
tax_units:
tax_unit:
members: [person1, person2]
state_filing_status_if_married_filing_separately_on_same_return: SEPARATE
households:
household:
members: [person1, person2]
state_code: MT
output:
mt_capital_gains_tax_indiv: [1_120, 0]
Original file line number Diff line number Diff line change
Expand Up @@ -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
# Form 2 line 2 uses the positive net LTCG amount, not gross 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(30K, 100K) = 30K
# excess_over_taxable = max(100K - 30K, 0) = 70K
# excess_over_threshold = max(41K - 70K, 0) = 0
# All 30K taxed at higher rate: 30K * 0.041 = 1230
mt_capital_gains_tax_joint: 1_230
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from policyengine_core.model_api import *
from policyengine_core.periods import instant


def modify_parameters(parameters):
parameters.gov.states.mt.tax.income.married_filing_separately_on_same_return_allowed.update(
start=instant("2024-01-01"), stop=instant("2024-12-31"), value=True
)
return parameters


class enable_mt_mfss_2024_reform(Reform):
def apply(self):
self.modify_parameters(modify_parameters)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -371,3 +371,143 @@
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 → Form 2 line 2 uses only the net LTCG amount
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: 3_685
mt_capital_gains_tax_joint: 820
mt_income_tax: 4_505

- 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
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,27 @@
output:
mt_capital_gains_tax_indiv: [0, 0]
mt_regular_income_tax_indiv: [0, 0]

- name: Indiv regular tax subtracts net LTCG after STCG losses when MFSS is enabled
period: 2024
reforms: policyengine_us.tests.policy.baseline.gov.states.mt.tax.income.enable_mt_mfss_2024_reform.enable_mt_mfss_2024_reform
input:
people:
person1:
mt_taxable_income_indiv: 80_000
long_term_capital_gains: 50_000
short_term_capital_gains: -20_000
person2:
mt_taxable_income_indiv: 0
long_term_capital_gains: 0
short_term_capital_gains: 0
tax_units:
tax_unit:
members: [person1, person2]
state_filing_status_if_married_filing_separately_on_same_return: SEPARATE
households:
household:
members: [person1, person2]
state_code: MT
output:
mt_regular_income_tax_indiv: [2_704, 0]
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ class mt_capital_gains_tax_applicable_threshold_indiv(Variable):

def formula(person, period, parameters):
p = parameters(period).gov.states.mt.tax.income.main.capital_gains
capital_gains = person("long_term_capital_gains", period)
ltcg = person("long_term_capital_gains", period)
stcg = person("short_term_capital_gains", period)
capital_gains = max_(min_(ltcg, ltcg + stcg), 0)
taxable_income = person("mt_taxable_income_indiv", period)
filing_status = person.tax_unit(
"state_filing_status_if_married_filing_separately_on_same_return",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,20 @@

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,
)
ltcg = add(tax_unit, period, ["long_term_capital_gains"])
stcg = add(tax_unit, period, ["short_term_capital_gains"])
capital_gains = max_(min_(ltcg, ltcg + stcg), 0)
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ 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:
capital_gains = person("long_term_capital_gains", period)
# No tax on zero or negative capital gains
ltcg = person("long_term_capital_gains", period)
stcg = person("short_term_capital_gains", period)
net_cg = ltcg + stcg
capital_gains = max_(min_(ltcg, net_cg), 0)
filing_status = person.tax_unit(
"state_filing_status_if_married_filing_separately_on_same_return",
period,
Expand Down
Loading
Loading