Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Biden reform to tax LTCG and qualified dividends as ordinary income for high-income filers #4295

Merged
merged 7 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- bump: minor
changes:
added:
- Biden reform to tax LTCG and qualified dividends as ordinary income for high-income filers.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
description: Presedent Biden proposed an increase of the capital gains tax rate for filers with taxable income above this threshold, based on filing status.
PavelMakarchuk marked this conversation as resolved.
Show resolved Hide resolved
metadata:
reference:
- title: General Explanations of the Administration's Fiscal Year 2025 Revenue Proposals
href: https://home.treasury.gov/system/files/131/General-Explanations-FY2025.pdf#page=88
label: President Biden capital gains tax rate increase threshold
PavelMakarchuk marked this conversation as resolved.
Show resolved Hide resolved
unit: currency-USD
period: year
breakdown:
- filing_status
JOINT:
2024-01-01: 0 # $1,000,000
SINGLE:
2024-01-01: 0 # $1,000,000
SEPARATE:
2024-01-01: 0 # $500,000
HEAD_OF_HOUSEHOLD:
2024-01-01: 0 # $1,000,000
WIDOW:
2024-01-01: 0 # $1,000,000
4 changes: 4 additions & 0 deletions policyengine_us/reforms/biden/budget_2025/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from .medicare_and_investment_tax_increase import (
create_medicare_and_investment_tax_increase_reform,
)

from .capital_gains_tax_increase import (
create_capital_gains_tax_increase_reform,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
from policyengine_us.model_api import *


def create_capital_gains_tax_increase() -> Reform:
class capital_gains_tax(Variable):
value_type = float
entity = TaxUnit
label = "Maximum income tax after capital gains tax"
unit = USD
definition_period = YEAR

def formula(tax_unit, period, parameters):
net_cg = tax_unit("net_capital_gain", period)
taxable_income = tax_unit("taxable_income", period)
adjusted_net_cg = min_(
tax_unit("adjusted_net_capital_gain", period),
taxable_income,
) # ANCG is referred to in all cases as ANCG or taxable income if less.

cg = parameters(period).gov.irs.capital_gains

excluded_cg = tax_unit(
"capital_gains_excluded_from_taxable_income", period
)
non_cg_taxable_income = max_(0, taxable_income - excluded_cg)
income_less_ancg = max_(0, taxable_income - adjusted_net_cg)

filing_status = tax_unit("filing_status", period)

first_threshold = cg.brackets.thresholds["1"][filing_status]
second_threshold = cg.brackets.thresholds["2"][filing_status]

income_ordinarily_under_second_rate = clip(
taxable_income, 0, first_threshold
)

income_ordinarily_under_third_rate = clip(
taxable_income, 0, second_threshold
)

# Under the 2025 bidem reform, long_term cg and dividends are
PavelMakarchuk marked this conversation as resolved.
Show resolved Hide resolved
# taxed at the income tax rates for filers with income over $1M
p_reform = parameters(
period
).gov.contrib.biden.budget_2025.capital_gains
income_threshold = p_reform.income_threshold[filing_status]
excess_income = max_(0, taxable_income - income_threshold)

cg_in_top_bracket = min_(adjusted_net_cg, excess_income)

income_tax = parameters(period).gov.irs.income.bracket.rates["7"]

new_cg_tax = cg_in_top_bracket * income_tax

cg_in_first_bracket = max_(
0, income_ordinarily_under_second_rate - income_less_ancg
)

cg_in_first_bracket_below_top_bracket = max_(
cg_in_first_bracket - cg_in_top_bracket, 0
)

cg_in_second_bracket = min_(
max_(
0, adjusted_net_cg - cg_in_first_bracket_below_top_bracket
),
max_(
0,
income_ordinarily_under_third_rate
- (
non_cg_taxable_income
+ cg_in_first_bracket_below_top_bracket
),
),
)

cg_in_second_bracket_below_top_bracket = max_(
cg_in_second_bracket - cg_in_top_bracket, 0
)

cg_in_third_bracket = max_(
adjusted_net_cg
- cg_in_first_bracket_below_top_bracket
- cg_in_second_bracket_below_top_bracket,
0,
)

cg_in_third_bracket_below_top_bracket = max_(
cg_in_third_bracket - cg_in_top_bracket, 0
)

main_cg_tax = (
cg_in_first_bracket_below_top_bracket * cg.brackets.rates["1"]
+ cg_in_second_bracket_below_top_bracket
* cg.brackets.rates["2"]
+ cg_in_third_bracket_below_top_bracket
* cg.brackets.rates["3"]
+ new_cg_tax
)

unrecaptured_s_1250_gain = tax_unit(
"unrecaptured_section_1250_gain", period
)

qualified_dividends = add(
tax_unit, period, ["qualified_dividend_income"]
)

max_taxable_unrecaptured_gain = min_(
unrecaptured_s_1250_gain,
max_(0, net_cg - qualified_dividends),
)
unrecaptured_gain_deduction = max_(
non_cg_taxable_income + net_cg - taxable_income,
0,
)
taxable_unrecaptured_gain = max_(
max_taxable_unrecaptured_gain - unrecaptured_gain_deduction,
0,
)

unrecaptured_gain_tax = (
cg.unrecaptured_s_1250_rate * taxable_unrecaptured_gain
)

remaining_cg_tax = (
tax_unit("capital_gains_28_percent_rate_gain", period)
* cg.other_cg_rate
)
return main_cg_tax + unrecaptured_gain_tax + remaining_cg_tax

class reform(Reform):
def apply(self):
self.update_variable(capital_gains_tax)

return reform


def create_capital_gains_tax_increase_reform(
parameters, period, bypass: bool = False
):
if bypass:
return create_capital_gains_tax_increase()

p = parameters(period).gov.contrib.biden.budget_2025.capital_gains

if (
(p.income_threshold.JOINT > 0)
| (p.income_threshold.SEPARATE > 0)
| (p.income_threshold.WIDOW > 0)
| (p.income_threshold.SINGLE > 0)
| (p.income_threshold.SEPARATE > 0)
):
return create_capital_gains_tax_increase()
else:
return None


capital_gains_tax_increase = create_capital_gains_tax_increase_reform(
None, None, bypass=True
)
5 changes: 5 additions & 0 deletions policyengine_us/reforms/reforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .biden.budget_2025 import (
create_medicare_and_investment_tax_increase_reform,
)
from .biden.budget_2025 import create_capital_gains_tax_increase_reform
from policyengine_core.reforms import Reform
import warnings

Expand Down Expand Up @@ -50,6 +51,9 @@ def create_structural_reforms_from_parameters(parameters, period):
reported_state_income_tax = create_reported_state_income_tax_reform(
parameters, period
)
capital_gains_tax_increase = create_capital_gains_tax_increase_reform(
parameters, period
)

reforms = [
afa_reform,
Expand All @@ -63,6 +67,7 @@ def create_structural_reforms_from_parameters(parameters, period):
abolish_payroll_tax,
reported_state_income_tax,
medicare_and_investment_tax_increase,
capital_gains_tax_increase,
]
reforms = tuple(filter(lambda x: x is not None, reforms))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
- name: Household with income over threshold and $100,000 over excess dividend and cg income
period: 2024
reforms: policyengine_us.reforms.biden.budget_2025.capital_gains_tax_increase.capital_gains_tax_increase
input:
gov.contrib.biden.budget_2025.capital_gains.income_threshold.JOINT: 1_000_000
# Household.
people:
head:
age: 36
qualified_dividend_income: 0
long_term_capital_gains: 80_000
spouse:
age: 36
qualified_dividend_income: 120_000
long_term_capital_gains: 0
tax_units:
tax_unit:
members: [head, spouse]
taxable_income: 1_100_000
filing_status: JOINT
output:
# First 100,000 taxed at 20%
# Last 100,000 taxed at 37%
capital_gains_tax: 57_000 # 15,000 + 37,000

- name: Reform not applied
period: 2024
input:
# Household.
people:
head:
age: 36
qualified_dividend_income: 0
long_term_capital_gains: 80_000
spouse:
age: 36
qualified_dividend_income: 120_000
long_term_capital_gains: 0
tax_units:
tax_unit:
members: [head, spouse]
taxable_income: 1_100_000
filing_status: JOINT
output:
# 200,000 taxed at 20%
capital_gains_tax: 40_000
8 changes: 6 additions & 2 deletions test.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@
}
],
"source": [
"from policyengine_us.data import CPS_2022, EnhancedCPS_2022, CalibratedPUFExtendedCPS_2022\n",
"from policyengine_us.data import (\n",
" CPS_2022,\n",
" EnhancedCPS_2022,\n",
" CalibratedPUFExtendedCPS_2022,\n",
")\n",
"\n",
"CalibratedPUFExtendedCPS_2022().generate()\n",
"EnhancedCPS_2022().generate()\n"
"EnhancedCPS_2022().generate()"
]
},
{
Expand Down
Loading