Skip to content

Commit 299f565

Browse files
MaxGhenisclaude
andcommitted
Add tier-separated TOB revenue variables
Adds OASDI and Medicare HI specific trust fund revenue variables: - tob_revenue_oasdi: Tier 1 (0-50%) revenue - tob_revenue_medicare_hi: Tier 2 (50-85%) revenue Uses proportional allocation of total TOB revenue. Includes tier 1 and tier 2 taxable SS variables from PR PolicyEngine#6747. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 0fe782e commit 299f565

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-0
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
"""
2+
Test TOB revenue variable with labor supply responses.
3+
"""
4+
import pytest
5+
from policyengine_us import Microsimulation
6+
from policyengine_core.reforms import Reform
7+
8+
9+
def test_tob_revenue_baseline():
10+
"""TOB revenue should be positive in baseline."""
11+
sim = Microsimulation()
12+
tob = sim.calculate("tob_revenue_total", period=2026)
13+
assert tob.sum() > 0
14+
15+
16+
def test_tob_revenue_with_lsr():
17+
"""TOB revenue should work with labor supply responses."""
18+
lsr_params = {
19+
"gov.simulation.labor_supply_responses.elasticities.income": {
20+
"2024-01-01.2100-12-31": -0.05
21+
}
22+
}
23+
reform = Reform.from_dict(lsr_params, country_id="us")
24+
sim = Microsimulation(reform=reform)
25+
26+
# Should not raise RecursionError
27+
tob = sim.calculate("tob_revenue_total", period=2026)
28+
income_tax = sim.calculate("income_tax", period=2026)
29+
30+
assert tob.sum() > 0
31+
assert income_tax.sum() > 0
32+
33+
34+
if __name__ == "__main__":
35+
print("Testing TOB revenue...")
36+
test_tob_revenue_baseline()
37+
print("✓ Baseline works")
38+
test_tob_revenue_with_lsr()
39+
print("✓ LSR works")
40+
print("\n✅ All tests passed!")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from policyengine_us.model_api import *
2+
3+
4+
class taxable_social_security_tier_1(Variable):
5+
value_type = float
6+
entity = TaxUnit
7+
definition_period = YEAR
8+
label = "Taxable Social Security (tier 1)"
9+
documentation = "Taxable Social Security from 0-50% taxation tier, credited to OASDI trust funds"
10+
unit = USD
11+
reference = "https://www.law.cornell.edu/uscode/text/26/86#a_1"
12+
13+
def formula(tax_unit, period, parameters):
14+
p = parameters(period).gov.irs.social_security.taxability
15+
gross_ss = tax_unit("tax_unit_social_security", period)
16+
combined_income = tax_unit(
17+
"tax_unit_combined_income_for_social_security_taxability", period
18+
)
19+
filing_status = tax_unit("filing_status", period)
20+
status = filing_status.possible_values
21+
separate = filing_status == status.SEPARATE
22+
cohabitating = tax_unit("cohabitating_spouses", period)
23+
24+
base_amount = where(
25+
separate & cohabitating,
26+
p.threshold.base.separate_cohabitating,
27+
p.threshold.base.main[filing_status],
28+
)
29+
adjusted_base_amount = where(
30+
separate & cohabitating,
31+
p.threshold.adjusted_base.separate_cohabitating,
32+
p.threshold.adjusted_base.main[filing_status],
33+
)
34+
35+
under_first_threshold = combined_income < base_amount
36+
under_second_threshold = combined_income < adjusted_base_amount
37+
38+
combined_income_excess = tax_unit(
39+
"tax_unit_ss_combined_income_excess", period
40+
)
41+
42+
# Tier 1 amount (IRC §86(a)(1))
43+
amount_under_paragraph_1 = min_(
44+
p.rate.base.benefit_cap * gross_ss,
45+
p.rate.base.excess * combined_income_excess,
46+
)
47+
48+
# Bracket amount when in tier 2 (IRC §86(a)(2)(A)(ii))
49+
bracket_amount = min_(
50+
amount_under_paragraph_1,
51+
p.rate.additional.bracket * (adjusted_base_amount - base_amount),
52+
)
53+
54+
return select(
55+
[under_first_threshold, under_second_threshold],
56+
[0, amount_under_paragraph_1],
57+
default=bracket_amount,
58+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from policyengine_us.model_api import *
2+
3+
4+
class taxable_social_security_tier_2(Variable):
5+
value_type = float
6+
entity = TaxUnit
7+
definition_period = YEAR
8+
label = "Taxable Social Security (tier 2)"
9+
documentation = "Taxable Social Security from 50-85% taxation tier, credited to Medicare HI trust fund"
10+
unit = USD
11+
reference = "https://www.law.cornell.edu/uscode/text/26/86#a_2"
12+
13+
def formula(tax_unit, period, parameters):
14+
total_taxable = tax_unit("tax_unit_taxable_social_security", period)
15+
tier_1 = tax_unit("taxable_social_security_tier_1", period)
16+
return total_taxable - tier_1
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from policyengine_us.model_api import *
2+
3+
4+
class tob_revenue_medicare_hi(Variable):
5+
value_type = float
6+
entity = TaxUnit
7+
definition_period = YEAR
8+
label = "Medicare HI trust fund revenue from SS benefit taxation (tier 2)"
9+
documentation = "Tax revenue from tier 2 (50-85%) Social Security benefit taxation credited to Medicare HI trust fund"
10+
unit = USD
11+
12+
def formula(tax_unit, period, parameters):
13+
"""
14+
Calculate Medicare HI trust fund revenue from tier 2 SS taxation.
15+
16+
Allocates total TOB revenue to Medicare HI based on tier 2's proportion
17+
of total taxable SS.
18+
"""
19+
# Get total TOB revenue
20+
total_tob = tax_unit("tob_revenue_total", period)
21+
22+
# Get tier amounts
23+
tier1 = tax_unit("taxable_social_security_tier_1", period)
24+
tier2 = tax_unit("taxable_social_security_tier_2", period)
25+
total_taxable = tier1 + tier2
26+
27+
# Allocate total TOB based on tier 2 proportion
28+
# Use where to handle division by zero
29+
medicare_share = where(total_taxable > 0, tier2 / total_taxable, 0)
30+
31+
return total_tob * medicare_share
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from policyengine_us.model_api import *
2+
3+
4+
class tob_revenue_oasdi(Variable):
5+
value_type = float
6+
entity = TaxUnit
7+
definition_period = YEAR
8+
label = "OASDI trust fund revenue from SS benefit taxation (tier 1)"
9+
documentation = "Tax revenue from tier 1 (0-50%) Social Security benefit taxation credited to OASDI trust funds"
10+
unit = USD
11+
12+
def formula(tax_unit, period, parameters):
13+
"""
14+
Calculate OASDI trust fund revenue from tier 1 SS taxation.
15+
16+
Allocates total TOB revenue to OASDI based on tier 1's proportion
17+
of total taxable SS.
18+
"""
19+
# Get total TOB revenue
20+
total_tob = tax_unit("tob_revenue_total", period)
21+
22+
# Get tier amounts
23+
tier1 = tax_unit("taxable_social_security_tier_1", period)
24+
tier2 = tax_unit("taxable_social_security_tier_2", period)
25+
total_taxable = tier1 + tier2
26+
27+
# Allocate total TOB based on tier 1 proportion
28+
# Use where to handle division by zero
29+
oasdi_share = where(total_taxable > 0, tier1 / total_taxable, 0)
30+
31+
return total_tob * oasdi_share

0 commit comments

Comments
 (0)