Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
 into samantharudra/issue5223
  • Loading branch information
samantharudra committed Oct 29, 2024
2 parents b4cfa16 + f6d9619 commit 979d61b
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 3 deletions.
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.136.1] - 2024-10-29 19:55:24

### Changed

- Updated policyengine-us-data to 0.11.1
- Updated microdf-python to 0.4.3

## [1.136.0] - 2024-10-29 13:25:05

### Added

- Capital gains tax responses.

## [1.135.0] - 2024-10-28 21:33:47

### Added
Expand Down Expand Up @@ -10089,6 +10102,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0



[1.136.1]: https://github.com/PolicyEngine/policyengine-us/compare/1.136.0...1.136.1
[1.136.0]: https://github.com/PolicyEngine/policyengine-us/compare/1.135.0...1.136.0
[1.135.0]: https://github.com/PolicyEngine/policyengine-us/compare/1.134.0...1.135.0
[1.134.0]: https://github.com/PolicyEngine/policyengine-us/compare/1.133.0...1.134.0
[1.133.0]: https://github.com/PolicyEngine/policyengine-us/compare/1.132.0...1.133.0
Expand Down
11 changes: 11 additions & 0 deletions changelog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8554,3 +8554,14 @@
added:
- Remaining 2025 IRS tax posted parameter updates.
date: 2024-10-28 21:33:47
- bump: minor
changes:
added:
- Capital gains tax responses.
date: 2024-10-29 13:25:05
- bump: patch
changes:
changed:
- Updated policyengine-us-data to 0.11.1
- Updated microdf-python to 0.4.3
date: 2024-10-29 19:55:24
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
description: Elasticity of capital gains with respect to the capital gains marginal tax rate.
values:
2000-01-01: 0
metadata:
unit: /1
label: capital gains elasticity
22 changes: 22 additions & 0 deletions policyengine_us/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ def __init__(self, *args, **kwargs):
)
weekly_hours.delete_arrays(known_period)

# Capital gains responses

cg_holder = self.get_holder("long_term_capital_gains")
for known_period in cg_holder.get_known_periods():
array = cg_holder.get_array(known_period)
self.set_input(
"capital_gains_before_response", known_period, array
)
cg_holder.delete_arrays(known_period)


class Microsimulation(CoreMicrosimulation):
default_tax_benefit_system = CountryTaxBenefitSystem
Expand Down Expand Up @@ -169,6 +179,16 @@ def __init__(self, *args, **kwargs):
)
weekly_hours.delete_arrays(known_period)

# Capital gains responses

cg_holder = self.get_holder("long_term_capital_gains")
for known_period in cg_holder.get_known_periods():
array = cg_holder.get_array(known_period)
self.set_input(
"capital_gains_before_response", known_period, array
)
cg_holder.delete_arrays(known_period)

self.input_variables = [
variable
for variable in self.input_variables
Expand All @@ -177,11 +197,13 @@ def __init__(self, *args, **kwargs):
"employment_income",
"self_employment_income",
"weekly_hours_worked",
"capital_gains",
]
] + [
"employment_income_before_lsr",
"self_employment_income_before_lsr",
"weekly_hours_worked_before_lsr",
"capital_gains_before_response",
]


Expand Down
175 changes: 175 additions & 0 deletions policyengine_us/variables/gov/simulation/capital_gains_responses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
from policyengine_us.model_api import *
from policyengine_core.simulations import *


class relative_capital_gains_mtr_change(Variable):
value_type = float
entity = Person
label = "relative change in capital gains tax rate"
unit = "/1"
definition_period = YEAR

def formula(person, period, parameters):
simulation: Simulation = person.simulation
baseline_branch = simulation.get_branch("baseline").get_branch(
"baseline_cgr_measurement", clone_system=True
)
baseline_person = baseline_branch.populations["person"]
baseline_branch.tax_benefit_system.neutralize_variable(
"capital_gains_behavioral_response"
)
baseline_branch.set_input(
"capital_gains_before_response",
period,
person("capital_gains_before_response", period),
)
baseline_mtr = baseline_person(
"marginal_tax_rate_on_capital_gains", period
)
del simulation.branches["baseline"].branches[
"baseline_cgr_measurement"
]

measurement_branch = simulation.get_branch(
"cgr_measurement", clone_system=True
)
measurement_branch.tax_benefit_system.neutralize_variable(
"capital_gains_behavioral_response"
)
measurement_branch.set_input(
"capital_gains_before_response",
period,
person("capital_gains_before_response", period),
)
measurement_person = measurement_branch.populations["person"]
reform_mtr = measurement_person(
"marginal_tax_rate_on_capital_gains", period
)
del simulation.branches["cgr_measurement"]

# Handle zeros in tax rates to prevent log(0)
min_rate = 0.001
baseline_mtr_adj = np.maximum(baseline_mtr, min_rate)
reform_mtr_adj = np.maximum(reform_mtr, min_rate)

# Calculate log difference
return np.log(reform_mtr_adj) - np.log(baseline_mtr_adj)


class capital_gains_elasticity(Variable):
value_type = float
entity = Person
label = "elasticity of capital gains realizations"
unit = "/1"
definition_period = YEAR

def formula(person, period, parameters):
gov = parameters(period).gov
return gov.simulation.capital_gains_responses.elasticity


class capital_gains_behavioral_response(Variable):
value_type = float
entity = Person
label = "capital gains behavioral response"
unit = USD
definition_period = YEAR

def formula(person, period, parameters):
simulation = person.simulation
if simulation.baseline is None:
return 0

if (
parameters(
period
).gov.simulation.capital_gains_responses.elasticity
== 0
):
return 0

capital_gains = person("capital_gains_before_response", period)
tax_rate_change = person("relative_capital_gains_mtr_change", period)
elasticity = person("capital_gains_elasticity", period)

# Calculate response using log differences
response_factor = np.exp(elasticity * tax_rate_change) - 1
response = capital_gains * response_factor

print(response.sum())

return response


class capital_gains_before_response(Variable):
label = "capital gains before responses"
entity = Person
definition_period = YEAR
value_type = float
unit = USD
uprating = "calibration.gov.irs.soi.long_term_capital_gains"


class adult_index_cg(Variable):
value_type = int
entity = Person
label = "index of adult in household, ranked by capital gains"
definition_period = YEAR

def formula(person, period, parameters):
return (
person.get_rank(
person.household,
-person("capital_gains_before_response", period),
condition=person("is_adult", period),
)
+ 1
)


class marginal_tax_rate_on_capital_gains(Variable):
label = "capital gains marginal tax rate"
documentation = "Percent of marginal capital gains that do not increase household net income."
entity = Person
definition_period = YEAR
value_type = float
unit = "/1"

def formula(person, period, parameters):
mtr_values = np.zeros(person.count, dtype=np.float32)
simulation = person.simulation
DELTA = 1_000
adult_index_values = person("adult_index_cg", period)
for adult_index in [1, 2]:
alt_simulation = simulation.get_branch(
f"adult_{adult_index}_cg_rise"
)
mask = adult_index_values == adult_index
for variable in simulation.tax_benefit_system.variables:
variable_data = simulation.tax_benefit_system.variables[
variable
]
if (
variable not in simulation.input_variables
and not variable_data.is_input_variable()
):
alt_simulation.delete_arrays(variable)
alt_simulation.set_input(
"capital_gains",
period,
person("capital_gains", period) + mask * DELTA,
)
alt_person = alt_simulation.person
household_net_income = person.household(
"household_net_income", period
)
household_net_income_higher_earnings = alt_person.household(
"household_net_income", period
)
increase = (
household_net_income_higher_earnings - household_net_income
)
mtr_values += where(mask, 1 - increase / DELTA, 0)

del simulation.branches[f"adult_{adult_index}_cg_rise"]
return mtr_values
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ class long_term_capital_gains(Variable):
href="https://www.law.cornell.edu/uscode/text/26/1222#3",
)
uprating = "calibration.gov.irs.soi.long_term_capital_gains"
adds = [
"capital_gains_before_response",
"capital_gains_behavioral_response",
]
3 changes: 3 additions & 0 deletions policyengine_us/variables/input/geography.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
medicaid_rating_areas,
second_lowest_silver_plan_cost,
)
import warnings

warnings.filterwarnings("ignore")

label = "Geography"

Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

setup(
name="policyengine-us",
version="1.135.0",
version="1.136.1",
author="PolicyEngine",
author_email="[email protected]",
long_description=readme,
Expand Down Expand Up @@ -35,8 +35,8 @@
],
install_requires=[
"policyengine-core>=3.10.0",
"policyengine-us-data==1.11.0",
"microdf-python",
"policyengine-us-data==1.11.1",
"microdf-python>=0.4.3",
"tqdm",
],
extras_require={
Expand Down

0 comments on commit 979d61b

Please sign in to comment.