policyengine-us 1.351.4__py3-none-any.whl → 1.352.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of policyengine-us might be problematic. Click here for more details.
- policyengine_us/reforms/reforms.py +0 -7
- policyengine_us/tests/utilities/test_ucgid_hierarchical.py +58 -0
- policyengine_us/variables/household/demographic/geographic/ucgid/ucgid.py +97 -0
- policyengine_us/variables/household/demographic/geographic/ucgid/ucgid_enum.py +551 -0
- policyengine_us/variables/household/demographic/geographic/ucgid/ucgid_str.py +12 -0
- policyengine_us/variables/household/demographic/household/household_count.py +9 -0
- policyengine_us/variables/household/demographic/spm_unit/spm_unit_count.py +9 -0
- policyengine_us/variables/household/demographic/tax_unit/tax_unit_count.py +9 -0
- {policyengine_us-1.351.4.dist-info → policyengine_us-1.352.0.dist-info}/METADATA +1 -1
- {policyengine_us-1.351.4.dist-info → policyengine_us-1.352.0.dist-info}/RECORD +13 -9
- policyengine_us/reforms/second_earner/__init__.py +0 -3
- policyengine_us/reforms/second_earner/second_earner_tax_reform.py +0 -820
- policyengine_us/tests/policy/contrib/second_earner/second_earner_tax_reform.yaml +0 -371
- {policyengine_us-1.351.4.dist-info → policyengine_us-1.352.0.dist-info}/WHEEL +0 -0
- {policyengine_us-1.351.4.dist-info → policyengine_us-1.352.0.dist-info}/entry_points.txt +0 -0
- {policyengine_us-1.351.4.dist-info → policyengine_us-1.352.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,820 +0,0 @@
|
|
|
1
|
-
from policyengine_us.model_api import *
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def create_second_earner_tax() -> Reform:
|
|
5
|
-
|
|
6
|
-
class is_primary_earner(Variable):
|
|
7
|
-
value_type = bool
|
|
8
|
-
entity = Person
|
|
9
|
-
label = "Whether this person is the primary earner in their tax unit"
|
|
10
|
-
definition_period = YEAR
|
|
11
|
-
|
|
12
|
-
def formula(person, period, parameters):
|
|
13
|
-
earned_income = person("earned_income", period)
|
|
14
|
-
is_tax_unit_head_or_spouse = person(
|
|
15
|
-
"is_tax_unit_head_or_spouse", period
|
|
16
|
-
)
|
|
17
|
-
is_tax_unit_head = person("is_tax_unit_head", period)
|
|
18
|
-
is_tax_unit_dependent = person("is_tax_unit_dependent", period)
|
|
19
|
-
|
|
20
|
-
# Add dependent income to head's income
|
|
21
|
-
dependent_income = (earned_income * is_tax_unit_dependent).sum()
|
|
22
|
-
earner_income = earned_income * is_tax_unit_head_or_spouse
|
|
23
|
-
earner_income = where(
|
|
24
|
-
is_tax_unit_head,
|
|
25
|
-
earner_income + dependent_income,
|
|
26
|
-
earner_income,
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
max_income = person.tax_unit.max(earner_income)
|
|
30
|
-
return (earner_income == max_income) & (
|
|
31
|
-
(
|
|
32
|
-
earner_income
|
|
33
|
-
> person.tax_unit.max(
|
|
34
|
-
where(~is_tax_unit_head, earner_income, 0)
|
|
35
|
-
)
|
|
36
|
-
)
|
|
37
|
-
| is_tax_unit_head
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
class taxable_income_person(Variable):
|
|
41
|
-
value_type = float
|
|
42
|
-
entity = Person
|
|
43
|
-
label = "IRS taxable income for each person"
|
|
44
|
-
unit = USD
|
|
45
|
-
definition_period = YEAR
|
|
46
|
-
|
|
47
|
-
def formula(person, period, parameters):
|
|
48
|
-
agi = person("adjusted_gross_income_person", period)
|
|
49
|
-
exemption_amount = person.tax_unit("exemptions", period)
|
|
50
|
-
is_joint = person.tax_unit("tax_unit_is_joint", period)
|
|
51
|
-
exemptions = where(
|
|
52
|
-
is_joint, exemption_amount / 2, exemption_amount
|
|
53
|
-
)
|
|
54
|
-
deductions = person("taxable_income_deductions_person", period)
|
|
55
|
-
return max_(0, agi - exemptions - deductions)
|
|
56
|
-
|
|
57
|
-
class income_tax_main_rates(Variable):
|
|
58
|
-
value_type = float
|
|
59
|
-
entity = TaxUnit
|
|
60
|
-
definition_period = YEAR
|
|
61
|
-
label = "Income tax main rates"
|
|
62
|
-
reference = "https://www.law.cornell.edu/uscode/text/26/1"
|
|
63
|
-
unit = USD
|
|
64
|
-
|
|
65
|
-
def formula(tax_unit, period, parameters):
|
|
66
|
-
person = tax_unit.members
|
|
67
|
-
full_taxable_income = person("taxable_income_person", period)
|
|
68
|
-
is_tax_unit_head_or_spouse = person(
|
|
69
|
-
"is_tax_unit_head_or_spouse", period
|
|
70
|
-
)
|
|
71
|
-
cg_exclusion = (
|
|
72
|
-
tax_unit("capital_gains_excluded_from_taxable_income", period)
|
|
73
|
-
/ 2
|
|
74
|
-
) * is_tax_unit_head_or_spouse
|
|
75
|
-
taxinc = max_(0, full_taxable_income - cg_exclusion)
|
|
76
|
-
p = parameters(period).gov.irs.income
|
|
77
|
-
bracket_tops = p.bracket.thresholds
|
|
78
|
-
bracket_rates = p.bracket.rates
|
|
79
|
-
filing_status = tax_unit("filing_status", period)
|
|
80
|
-
|
|
81
|
-
# Determine primary and secondary earner incomes based on income size
|
|
82
|
-
is_tax_unit_dependent = person("is_tax_unit_dependent", period)
|
|
83
|
-
|
|
84
|
-
# Add dependent income to head's income
|
|
85
|
-
dependent_income = (taxinc * is_tax_unit_dependent).sum()
|
|
86
|
-
earner_taxinc = taxinc * is_tax_unit_head_or_spouse
|
|
87
|
-
|
|
88
|
-
is_primary_earner = person("is_primary_earner", period)
|
|
89
|
-
is_secondary_earner = (
|
|
90
|
-
is_tax_unit_head_or_spouse & ~is_primary_earner
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
taxable_income_primary_earner = where(
|
|
94
|
-
is_primary_earner, earner_taxinc + dependent_income, 0
|
|
95
|
-
).sum()
|
|
96
|
-
taxable_income_secondary_earner = where(
|
|
97
|
-
is_secondary_earner, earner_taxinc, 0
|
|
98
|
-
).sum()
|
|
99
|
-
|
|
100
|
-
# Calculate primary earner tax using actual filing status
|
|
101
|
-
primary_earner_tax = 0
|
|
102
|
-
bracket_bottom = 0
|
|
103
|
-
for i in range(1, len(list(bracket_rates.__iter__())) + 1):
|
|
104
|
-
b = str(i)
|
|
105
|
-
bracket_top = bracket_tops[b][filing_status]
|
|
106
|
-
primary_earner_tax += bracket_rates[b] * amount_between(
|
|
107
|
-
taxable_income_primary_earner, bracket_bottom, bracket_top
|
|
108
|
-
)
|
|
109
|
-
bracket_bottom = bracket_top
|
|
110
|
-
|
|
111
|
-
# Calculate secondary earner tax using single filing status
|
|
112
|
-
secondary_earner_tax = 0
|
|
113
|
-
bracket_bottom = 0
|
|
114
|
-
single_status = "SINGLE"
|
|
115
|
-
for i in range(1, len(list(bracket_rates.__iter__())) + 1):
|
|
116
|
-
b = str(i)
|
|
117
|
-
bracket_top = bracket_tops[b][single_status]
|
|
118
|
-
secondary_earner_tax += bracket_rates[b] * amount_between(
|
|
119
|
-
taxable_income_secondary_earner,
|
|
120
|
-
bracket_bottom,
|
|
121
|
-
bracket_top,
|
|
122
|
-
)
|
|
123
|
-
bracket_bottom = bracket_top
|
|
124
|
-
|
|
125
|
-
return primary_earner_tax + secondary_earner_tax
|
|
126
|
-
|
|
127
|
-
class basic_standard_deduction_person(Variable):
|
|
128
|
-
value_type = float
|
|
129
|
-
entity = Person
|
|
130
|
-
label = "Basic standard deduction"
|
|
131
|
-
definition_period = YEAR
|
|
132
|
-
unit = USD
|
|
133
|
-
reference = "https://www.law.cornell.edu/uscode/text/26/63#c_2"
|
|
134
|
-
|
|
135
|
-
def formula(person, period, parameters):
|
|
136
|
-
std = parameters(period).gov.irs.deductions.standard
|
|
137
|
-
filing_status = person.tax_unit("filing_status", period)
|
|
138
|
-
separate_filer_itemizes = person.tax_unit(
|
|
139
|
-
"separate_filer_itemizes", period
|
|
140
|
-
)
|
|
141
|
-
dependent_elsewhere = person.tax_unit(
|
|
142
|
-
"head_is_dependent_elsewhere", period
|
|
143
|
-
)
|
|
144
|
-
# Determine primary and secondary earners
|
|
145
|
-
is_tax_unit_head_or_spouse = person(
|
|
146
|
-
"is_tax_unit_head_or_spouse", period
|
|
147
|
-
)
|
|
148
|
-
is_primary_earner = person("is_primary_earner", period)
|
|
149
|
-
is_secondary_earner = (
|
|
150
|
-
is_tax_unit_head_or_spouse & ~is_primary_earner
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
# Calculate primary earner deduction using actual filing status
|
|
154
|
-
primary_deduction = std.amount[filing_status]
|
|
155
|
-
|
|
156
|
-
# Calculate secondary earner deduction using single filing status
|
|
157
|
-
secondary_deduction = std.amount["SINGLE"]
|
|
158
|
-
# Combine deductions based on earner status
|
|
159
|
-
standard_deduction = where(
|
|
160
|
-
is_primary_earner,
|
|
161
|
-
primary_deduction,
|
|
162
|
-
where(is_secondary_earner, secondary_deduction, 0),
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
standard_deduction_if_dependent = min_(
|
|
166
|
-
standard_deduction,
|
|
167
|
-
max_(
|
|
168
|
-
std.dependent.additional_earned_income
|
|
169
|
-
+ person.tax_unit("tax_unit_earned_income", period),
|
|
170
|
-
std.dependent.amount,
|
|
171
|
-
),
|
|
172
|
-
)
|
|
173
|
-
|
|
174
|
-
return select(
|
|
175
|
-
[
|
|
176
|
-
separate_filer_itemizes,
|
|
177
|
-
dependent_elsewhere,
|
|
178
|
-
True,
|
|
179
|
-
],
|
|
180
|
-
[
|
|
181
|
-
0,
|
|
182
|
-
standard_deduction_if_dependent,
|
|
183
|
-
standard_deduction,
|
|
184
|
-
],
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
class additional_standard_deduction_person(Variable):
|
|
188
|
-
value_type = float
|
|
189
|
-
entity = Person
|
|
190
|
-
label = "Additional standard deduction for each person"
|
|
191
|
-
unit = USD
|
|
192
|
-
definition_period = YEAR
|
|
193
|
-
reference = "https://www.law.cornell.edu/uscode/text/26/63#f"
|
|
194
|
-
|
|
195
|
-
def formula(person, period, parameters):
|
|
196
|
-
std = parameters(period).gov.irs.deductions.standard
|
|
197
|
-
filing_status = person.tax_unit("filing_status", period)
|
|
198
|
-
is_blind = person("is_blind", period).astype(int)
|
|
199
|
-
age_threshold = parameters(
|
|
200
|
-
period
|
|
201
|
-
).gov.irs.deductions.standard.aged_or_blind.age_threshold
|
|
202
|
-
is_aged = (person("age", period) >= age_threshold).astype(int)
|
|
203
|
-
aged_blind = is_blind + is_aged
|
|
204
|
-
primary_earner = person("is_primary_earner", period)
|
|
205
|
-
amount = where(
|
|
206
|
-
primary_earner,
|
|
207
|
-
std.aged_or_blind.amount[filing_status],
|
|
208
|
-
std.aged_or_blind.amount["SINGLE"],
|
|
209
|
-
)
|
|
210
|
-
return aged_blind * amount
|
|
211
|
-
|
|
212
|
-
class bonus_guaranteed_deduction_person(Variable):
|
|
213
|
-
value_type = float
|
|
214
|
-
entity = Person
|
|
215
|
-
label = "Bonus guaranteed deduction"
|
|
216
|
-
unit = USD
|
|
217
|
-
definition_period = YEAR
|
|
218
|
-
reference = "https://waysandmeans.house.gov/malliotakis-steel-lead-legislation-to-provide-tax-relief-to-working-families/"
|
|
219
|
-
|
|
220
|
-
def formula(person, period, parameters):
|
|
221
|
-
filing_status = person.tax_unit("filing_status", period)
|
|
222
|
-
wftca = parameters(
|
|
223
|
-
period
|
|
224
|
-
).gov.contrib.congress.wftca.bonus_guaranteed_deduction
|
|
225
|
-
primary_earner = person("is_primary_earner", period)
|
|
226
|
-
amount = where(
|
|
227
|
-
primary_earner,
|
|
228
|
-
wftca.amount[filing_status],
|
|
229
|
-
wftca.amount["SINGLE"],
|
|
230
|
-
)
|
|
231
|
-
agi = person("adjusted_gross_income_person", period)
|
|
232
|
-
threshold = where(
|
|
233
|
-
primary_earner,
|
|
234
|
-
wftca.phase_out.threshold[filing_status],
|
|
235
|
-
wftca.phase_out.threshold["SINGLE"],
|
|
236
|
-
)
|
|
237
|
-
income_in_phase_out_region = max_(agi - threshold, 0)
|
|
238
|
-
reduction = wftca.phase_out.rate * income_in_phase_out_region
|
|
239
|
-
return max_(amount - reduction, 0)
|
|
240
|
-
|
|
241
|
-
class standard_deduction_person(Variable):
|
|
242
|
-
value_type = float
|
|
243
|
-
entity = Person
|
|
244
|
-
label = "Standard deduction for each person"
|
|
245
|
-
unit = USD
|
|
246
|
-
definition_period = YEAR
|
|
247
|
-
reference = "https://www.law.cornell.edu/uscode/text/26/63#c"
|
|
248
|
-
|
|
249
|
-
def formula(person, period, parameters):
|
|
250
|
-
basic_deduction = person("basic_standard_deduction_person", period)
|
|
251
|
-
additional_deduction = person(
|
|
252
|
-
"additional_standard_deduction_person", period
|
|
253
|
-
)
|
|
254
|
-
bonus_deduction = person(
|
|
255
|
-
"bonus_guaranteed_deduction_person", period
|
|
256
|
-
)
|
|
257
|
-
return basic_deduction + additional_deduction + bonus_deduction
|
|
258
|
-
|
|
259
|
-
class taxable_income_deductions_person(Variable):
|
|
260
|
-
value_type = float
|
|
261
|
-
entity = Person
|
|
262
|
-
label = "Taxable income deductions for each person"
|
|
263
|
-
unit = USD
|
|
264
|
-
definition_period = YEAR
|
|
265
|
-
|
|
266
|
-
def formula(person, period, parameters):
|
|
267
|
-
itemizes = person.tax_unit("tax_unit_itemizes", period)
|
|
268
|
-
is_joint = person.tax_unit("tax_unit_is_joint", period)
|
|
269
|
-
deductions_if_itemizing_amount = person.tax_unit(
|
|
270
|
-
"taxable_income_deductions_if_itemizing", period
|
|
271
|
-
)
|
|
272
|
-
deductions_if_itemizing = where(
|
|
273
|
-
is_joint,
|
|
274
|
-
deductions_if_itemizing_amount / 2,
|
|
275
|
-
deductions_if_itemizing_amount,
|
|
276
|
-
)
|
|
277
|
-
standard_deduction = person("standard_deduction_person", period)
|
|
278
|
-
qbid = person("qualified_business_income_deduction_person", period)
|
|
279
|
-
return where(
|
|
280
|
-
itemizes, deductions_if_itemizing, standard_deduction + qbid
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
class net_capital_gain_person(Variable):
|
|
284
|
-
value_type = float
|
|
285
|
-
entity = Person
|
|
286
|
-
label = "Net capital gain"
|
|
287
|
-
unit = USD
|
|
288
|
-
documentation = (
|
|
289
|
-
"The excess of net long-term capital gain over net short-term capital"
|
|
290
|
-
'loss, plus qualified dividends (the definition of "net capital gain"'
|
|
291
|
-
"which applies to 26 U.S.C. § 1(h) from § 1(h)(11))."
|
|
292
|
-
)
|
|
293
|
-
definition_period = YEAR
|
|
294
|
-
reference = dict(
|
|
295
|
-
title="26 U.S. Code § 1222(11)",
|
|
296
|
-
href="https://www.law.cornell.edu/uscode/text/26/1222#11",
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
def formula(person, period, parameters):
|
|
300
|
-
lt_capital_gain = person("long_term_capital_gains", period)
|
|
301
|
-
st_capital_loss = -person("short_term_capital_gains", period)
|
|
302
|
-
net_cap_gain = max_(0, lt_capital_gain - st_capital_loss)
|
|
303
|
-
qual_div_income = person("qualified_dividend_income", period)
|
|
304
|
-
return net_cap_gain + qual_div_income
|
|
305
|
-
|
|
306
|
-
class adjusted_net_capital_gain_person(Variable):
|
|
307
|
-
value_type = float
|
|
308
|
-
entity = Person
|
|
309
|
-
label = "Adjusted net capital gain"
|
|
310
|
-
unit = USD
|
|
311
|
-
documentation = "The excess of net long-term capital gain over net short-term capital loss."
|
|
312
|
-
definition_period = YEAR
|
|
313
|
-
reference = dict(
|
|
314
|
-
title="26 U.S. Code § 1(h)(3)",
|
|
315
|
-
href="https://www.law.cornell.edu/uscode/text/26/1#h_3",
|
|
316
|
-
)
|
|
317
|
-
defined_for = "is_tax_unit_head_or_spouse"
|
|
318
|
-
|
|
319
|
-
def formula(person, period, parameters):
|
|
320
|
-
net_capital_gain = person("net_capital_gain_person", period)
|
|
321
|
-
# The law actually uses the original definition of 'net capital gain' which does not include
|
|
322
|
-
# qualified dividend income, but separately adds qualified dividends here. The definition of
|
|
323
|
-
# 'net capital gain' in the variable 'net_capital_gain' actually has some very specific exclusion
|
|
324
|
-
# criteria for particular types of dividends and companies, so it's not an *exact* fit to the
|
|
325
|
-
# definition here, but it's a good enough approximation. See 26 U.S. Code § 1(h)(11)(B) for the
|
|
326
|
-
# definition of 'net capital gain' for the above variable, and 26 U.S. Code § 1(h)(3) for the definition
|
|
327
|
-
# of adjusted net capital gain (this variable).
|
|
328
|
-
qualified_dividend_income = person(
|
|
329
|
-
"qualified_dividend_income", period
|
|
330
|
-
)
|
|
331
|
-
is_joint = person.tax_unit("tax_unit_is_joint", period)
|
|
332
|
-
divisor = where(is_joint, 2, 1)
|
|
333
|
-
unrecaptured_s_1250_gain = (
|
|
334
|
-
person.tax_unit("unrecaptured_section_1250_gain", period)
|
|
335
|
-
/ divisor
|
|
336
|
-
)
|
|
337
|
-
cg_28_pct_rate_gain = (
|
|
338
|
-
person.tax_unit("capital_gains_28_percent_rate_gain", period)
|
|
339
|
-
/ divisor
|
|
340
|
-
)
|
|
341
|
-
net_gains_less_dividends = max_(
|
|
342
|
-
0,
|
|
343
|
-
net_capital_gain - qualified_dividend_income,
|
|
344
|
-
)
|
|
345
|
-
reduced_capital_gains = max_(
|
|
346
|
-
net_gains_less_dividends
|
|
347
|
-
- (unrecaptured_s_1250_gain + cg_28_pct_rate_gain),
|
|
348
|
-
0,
|
|
349
|
-
)
|
|
350
|
-
return reduced_capital_gains + qualified_dividend_income
|
|
351
|
-
|
|
352
|
-
class capital_gains_tax(Variable):
|
|
353
|
-
value_type = float
|
|
354
|
-
entity = TaxUnit
|
|
355
|
-
label = "Maximum income tax after capital gains tax"
|
|
356
|
-
unit = USD
|
|
357
|
-
definition_period = YEAR
|
|
358
|
-
|
|
359
|
-
def formula(tax_unit, period, parameters):
|
|
360
|
-
person = tax_unit.members
|
|
361
|
-
net_cg = person("net_capital_gain_person", period)
|
|
362
|
-
taxable_income = person("taxable_income_person", period)
|
|
363
|
-
adjusted_net_cg = min_(
|
|
364
|
-
person("adjusted_net_capital_gain_person", period),
|
|
365
|
-
taxable_income,
|
|
366
|
-
) # ANCG is referred to in all cases as ANCG or taxable income if less.
|
|
367
|
-
|
|
368
|
-
cg = parameters(period).gov.irs.capital_gains
|
|
369
|
-
|
|
370
|
-
excluded_cg = tax_unit(
|
|
371
|
-
"capital_gains_excluded_from_taxable_income", period
|
|
372
|
-
)
|
|
373
|
-
non_cg_taxable_income = max_(0, taxable_income - excluded_cg)
|
|
374
|
-
|
|
375
|
-
filing_status = tax_unit("filing_status", period)
|
|
376
|
-
is_tax_unit_head_or_spouse = person(
|
|
377
|
-
"is_tax_unit_head_or_spouse", period
|
|
378
|
-
)
|
|
379
|
-
is_primary_earner = person("is_primary_earner", period)
|
|
380
|
-
is_secondary_earner = (
|
|
381
|
-
is_tax_unit_head_or_spouse & ~is_primary_earner
|
|
382
|
-
)
|
|
383
|
-
is_tax_unit_dependent = person("is_tax_unit_dependent", period)
|
|
384
|
-
# Split capital gains between primary and secondary earners
|
|
385
|
-
primary_cg = where(is_primary_earner, adjusted_net_cg, 0)
|
|
386
|
-
secondary_cg = where(is_secondary_earner, adjusted_net_cg, 0)
|
|
387
|
-
dependent_cg = where(
|
|
388
|
-
is_tax_unit_dependent, adjusted_net_cg, 0
|
|
389
|
-
).sum()
|
|
390
|
-
primary_cg += dependent_cg # Add dependent gains to primary earner
|
|
391
|
-
|
|
392
|
-
# Calculate primary earner capital gains tax (using filing status thresholds)
|
|
393
|
-
first_threshold_primary = cg.brackets.thresholds["1"][
|
|
394
|
-
filing_status
|
|
395
|
-
]
|
|
396
|
-
second_threshold_primary = cg.brackets.thresholds["2"][
|
|
397
|
-
filing_status
|
|
398
|
-
]
|
|
399
|
-
|
|
400
|
-
# Calculate secondary earner capital gains tax (using single thresholds)
|
|
401
|
-
first_threshold_secondary = cg.brackets.thresholds["1"]["SINGLE"]
|
|
402
|
-
second_threshold_secondary = cg.brackets.thresholds["2"]["SINGLE"]
|
|
403
|
-
|
|
404
|
-
# Calculate brackets for primary earner
|
|
405
|
-
primary_cg_in_first = clip(primary_cg, 0, first_threshold_primary)
|
|
406
|
-
primary_cg_in_second = clip(
|
|
407
|
-
primary_cg - first_threshold_primary,
|
|
408
|
-
0,
|
|
409
|
-
second_threshold_primary - first_threshold_primary,
|
|
410
|
-
)
|
|
411
|
-
primary_cg_in_third = max_(
|
|
412
|
-
0, primary_cg - second_threshold_primary
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
# Calculate brackets for secondary earner
|
|
416
|
-
secondary_cg_in_first = clip(
|
|
417
|
-
secondary_cg, 0, first_threshold_secondary
|
|
418
|
-
)
|
|
419
|
-
secondary_cg_in_second = clip(
|
|
420
|
-
secondary_cg - first_threshold_secondary,
|
|
421
|
-
0,
|
|
422
|
-
second_threshold_secondary - first_threshold_secondary,
|
|
423
|
-
)
|
|
424
|
-
secondary_cg_in_third = max_(
|
|
425
|
-
0, secondary_cg - second_threshold_secondary
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
# Calculate total capital gains tax
|
|
429
|
-
main_cg_tax = (
|
|
430
|
-
(primary_cg_in_first + secondary_cg_in_first)
|
|
431
|
-
* cg.brackets.rates["1"]
|
|
432
|
-
+ (primary_cg_in_second + secondary_cg_in_second)
|
|
433
|
-
* cg.brackets.rates["2"]
|
|
434
|
-
+ (primary_cg_in_third + secondary_cg_in_third)
|
|
435
|
-
* cg.brackets.rates["3"]
|
|
436
|
-
)
|
|
437
|
-
is_joint = tax_unit("tax_unit_is_joint", period)
|
|
438
|
-
divisor = where(is_joint, 2, 1)
|
|
439
|
-
unrecaptured_s_1250_gain = (
|
|
440
|
-
tax_unit("unrecaptured_section_1250_gain", period) / divisor
|
|
441
|
-
)
|
|
442
|
-
qualified_dividends = (
|
|
443
|
-
add(tax_unit, period, ["qualified_dividend_income"]) / divisor
|
|
444
|
-
)
|
|
445
|
-
max_taxable_unrecaptured_gain = min_(
|
|
446
|
-
unrecaptured_s_1250_gain,
|
|
447
|
-
max_(0, net_cg - qualified_dividends),
|
|
448
|
-
)
|
|
449
|
-
unrecaptured_gain_deduction = max_(
|
|
450
|
-
non_cg_taxable_income + net_cg - taxable_income,
|
|
451
|
-
0,
|
|
452
|
-
)
|
|
453
|
-
taxable_unrecaptured_gain = max_(
|
|
454
|
-
max_taxable_unrecaptured_gain - unrecaptured_gain_deduction,
|
|
455
|
-
0,
|
|
456
|
-
)
|
|
457
|
-
|
|
458
|
-
unrecaptured_gain_tax = (
|
|
459
|
-
cg.unrecaptured_s_1250_rate * taxable_unrecaptured_gain
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
remaining_cg_tax = (
|
|
463
|
-
tax_unit("capital_gains_28_percent_rate_gain", period)
|
|
464
|
-
* cg.other_cg_rate
|
|
465
|
-
) / divisor
|
|
466
|
-
return tax_unit.sum(
|
|
467
|
-
main_cg_tax + unrecaptured_gain_tax + remaining_cg_tax
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
class amt_excluded_deductions_person(Variable):
|
|
471
|
-
value_type = float
|
|
472
|
-
entity = Person
|
|
473
|
-
definition_period = YEAR
|
|
474
|
-
label = "AMT taxable income excluded deductions"
|
|
475
|
-
unit = USD
|
|
476
|
-
reference = "https://www.law.cornell.edu/uscode/text/26/55#b_2"
|
|
477
|
-
|
|
478
|
-
def formula(person, period, parameters):
|
|
479
|
-
itemizing = person.tax_unit("tax_unit_itemizes", period)
|
|
480
|
-
standard_deduction = person("standard_deduction_person", period)
|
|
481
|
-
is_joint = person.tax_unit("tax_unit_is_joint", period)
|
|
482
|
-
divisor = where(is_joint, 2, 1)
|
|
483
|
-
salt_deduction = (
|
|
484
|
-
person.tax_unit("salt_deduction", period) / divisor
|
|
485
|
-
)
|
|
486
|
-
return where(itemizing, salt_deduction, standard_deduction)
|
|
487
|
-
|
|
488
|
-
class amt_income_person(Variable):
|
|
489
|
-
value_type = float
|
|
490
|
-
entity = Person
|
|
491
|
-
definition_period = YEAR
|
|
492
|
-
label = "AMT taxable income"
|
|
493
|
-
unit = USD
|
|
494
|
-
reference = "https://www.law.cornell.edu/uscode/text/26/55#b_2"
|
|
495
|
-
defined_for = "is_tax_unit_head_or_spouse"
|
|
496
|
-
|
|
497
|
-
def formula(person, period, parameters):
|
|
498
|
-
taxable_income = person("taxable_income_person", period)
|
|
499
|
-
deductions = person("amt_excluded_deductions_person", period)
|
|
500
|
-
is_joint = person.tax_unit("tax_unit_is_joint", period)
|
|
501
|
-
divisor = where(is_joint, 2, 1)
|
|
502
|
-
separate_addition = (
|
|
503
|
-
person.tax_unit("amt_separate_addition", period) / divisor
|
|
504
|
-
)
|
|
505
|
-
return taxable_income + deductions + separate_addition
|
|
506
|
-
|
|
507
|
-
class alternative_minimum_tax(Variable):
|
|
508
|
-
value_type = float
|
|
509
|
-
entity = TaxUnit
|
|
510
|
-
definition_period = YEAR
|
|
511
|
-
label = "Alternative Minimum Tax"
|
|
512
|
-
unit = USD
|
|
513
|
-
documentation = "Alternative Minimum Tax (AMT) liability"
|
|
514
|
-
|
|
515
|
-
def formula(tax_unit, period, parameters):
|
|
516
|
-
person = tax_unit.members
|
|
517
|
-
amt_income = person("amt_income_person", period)
|
|
518
|
-
# Form 6251, Part II top
|
|
519
|
-
p = parameters(period).gov.irs.income.amt
|
|
520
|
-
phase_out = p.exemption.phase_out
|
|
521
|
-
filing_status = tax_unit("filing_status", period)
|
|
522
|
-
|
|
523
|
-
# Split calculations for primary and secondary earners
|
|
524
|
-
is_primary_earner = person("is_primary_earner", period)
|
|
525
|
-
is_secondary_earner = (
|
|
526
|
-
person("is_tax_unit_head_or_spouse", period)
|
|
527
|
-
& ~is_primary_earner
|
|
528
|
-
)
|
|
529
|
-
is_tax_unit_dependent = person("is_tax_unit_dependent", period)
|
|
530
|
-
|
|
531
|
-
# Primary earner uses filing status thresholds
|
|
532
|
-
primary_base_exemption = p.exemption.amount[filing_status]
|
|
533
|
-
primary_phase_out_start = phase_out.start[filing_status]
|
|
534
|
-
|
|
535
|
-
# Secondary earner uses single thresholds
|
|
536
|
-
secondary_base_exemption = p.exemption.amount["SINGLE"]
|
|
537
|
-
secondary_phase_out_start = phase_out.start["SINGLE"]
|
|
538
|
-
|
|
539
|
-
# Calculate income for each earner
|
|
540
|
-
primary_income = where(is_primary_earner, amt_income, 0)
|
|
541
|
-
secondary_income = where(is_secondary_earner, amt_income, 0)
|
|
542
|
-
dependent_income = where(
|
|
543
|
-
is_tax_unit_dependent, amt_income, 0
|
|
544
|
-
).sum()
|
|
545
|
-
primary_income += (
|
|
546
|
-
dependent_income # Add dependent income to primary
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
# Calculate exemption amounts
|
|
550
|
-
primary_excess = max_(0, primary_income - primary_phase_out_start)
|
|
551
|
-
secondary_excess = max_(
|
|
552
|
-
0, secondary_income - secondary_phase_out_start
|
|
553
|
-
)
|
|
554
|
-
|
|
555
|
-
primary_exemption = max_(
|
|
556
|
-
0, primary_base_exemption - phase_out.rate * primary_excess
|
|
557
|
-
)
|
|
558
|
-
secondary_exemption = max_(
|
|
559
|
-
0, secondary_base_exemption - phase_out.rate * secondary_excess
|
|
560
|
-
)
|
|
561
|
-
|
|
562
|
-
age_head = tax_unit("age_head", period)
|
|
563
|
-
child = parameters(period).gov.irs.dependent.ineligible_age
|
|
564
|
-
young_head = (age_head != 0) & (age_head < child.non_student)
|
|
565
|
-
no_or_young_spouse = (
|
|
566
|
-
tax_unit("age_spouse", period) < child.non_student
|
|
567
|
-
)
|
|
568
|
-
adj_earnings = person("adjusted_earnings", period)
|
|
569
|
-
child_amount = p.exemption.child.amount
|
|
570
|
-
|
|
571
|
-
kiddie_tax_exemption_cap_applies = young_head & no_or_young_spouse
|
|
572
|
-
exemption_cap = where(
|
|
573
|
-
kiddie_tax_exemption_cap_applies,
|
|
574
|
-
adj_earnings + child_amount,
|
|
575
|
-
np.inf,
|
|
576
|
-
)
|
|
577
|
-
primary_exemption = min_(primary_exemption, exemption_cap)
|
|
578
|
-
secondary_exemption = min_(secondary_exemption, exemption_cap)
|
|
579
|
-
|
|
580
|
-
# Calculate taxable income
|
|
581
|
-
taxable_income = person("taxable_income_person", period)
|
|
582
|
-
# Do not add back deduction for filers subject to the kiddie tax
|
|
583
|
-
primary_applied_income = where(
|
|
584
|
-
kiddie_tax_exemption_cap_applies,
|
|
585
|
-
where(is_primary_earner, taxable_income, 0),
|
|
586
|
-
primary_income,
|
|
587
|
-
)
|
|
588
|
-
secondary_applied_income = where(
|
|
589
|
-
kiddie_tax_exemption_cap_applies,
|
|
590
|
-
where(is_secondary_earner, taxable_income, 0),
|
|
591
|
-
secondary_income,
|
|
592
|
-
)
|
|
593
|
-
|
|
594
|
-
primary_reduced_income = max_(
|
|
595
|
-
0, primary_applied_income - primary_exemption
|
|
596
|
-
)
|
|
597
|
-
secondary_reduced_income = max_(
|
|
598
|
-
0, secondary_applied_income - secondary_exemption
|
|
599
|
-
)
|
|
600
|
-
|
|
601
|
-
# Calculate bracket fractions
|
|
602
|
-
primary_bracket_fraction = where(
|
|
603
|
-
filing_status == filing_status.possible_values.SEPARATE,
|
|
604
|
-
0.5,
|
|
605
|
-
1.0,
|
|
606
|
-
)
|
|
607
|
-
secondary_bracket_fraction = (
|
|
608
|
-
1.0 # Single always uses full brackets
|
|
609
|
-
)
|
|
610
|
-
|
|
611
|
-
# Calculate tax thresholds
|
|
612
|
-
primary_tax_threshold = (
|
|
613
|
-
p.brackets.thresholds[-1] * primary_bracket_fraction
|
|
614
|
-
)
|
|
615
|
-
secondary_tax_threshold = (
|
|
616
|
-
p.brackets.thresholds[-1] * secondary_bracket_fraction
|
|
617
|
-
)
|
|
618
|
-
|
|
619
|
-
lower_rate = p.brackets.rates[0]
|
|
620
|
-
higher_rate = p.brackets.rates[1]
|
|
621
|
-
|
|
622
|
-
# Calculate tax for primary earner
|
|
623
|
-
primary_lower_tax = (
|
|
624
|
-
min_(primary_reduced_income, primary_tax_threshold)
|
|
625
|
-
* lower_rate
|
|
626
|
-
)
|
|
627
|
-
primary_higher_tax = (
|
|
628
|
-
max_(0, primary_reduced_income - primary_tax_threshold)
|
|
629
|
-
* higher_rate
|
|
630
|
-
)
|
|
631
|
-
|
|
632
|
-
# Calculate tax for secondary earner
|
|
633
|
-
secondary_lower_tax = (
|
|
634
|
-
min_(secondary_reduced_income, secondary_tax_threshold)
|
|
635
|
-
* lower_rate
|
|
636
|
-
)
|
|
637
|
-
secondary_higher_tax = (
|
|
638
|
-
max_(0, secondary_reduced_income - secondary_tax_threshold)
|
|
639
|
-
* higher_rate
|
|
640
|
-
)
|
|
641
|
-
|
|
642
|
-
# Combine taxes
|
|
643
|
-
reduced_income_tax = (
|
|
644
|
-
primary_lower_tax
|
|
645
|
-
+ primary_higher_tax
|
|
646
|
-
+ secondary_lower_tax
|
|
647
|
-
+ secondary_higher_tax
|
|
648
|
-
)
|
|
649
|
-
|
|
650
|
-
dwks10, dwks13, dwks14, dwks19, e24515 = [
|
|
651
|
-
add(tax_unit, period, [variable])
|
|
652
|
-
for variable in [
|
|
653
|
-
"dwks10",
|
|
654
|
-
"dwks13",
|
|
655
|
-
"dwks14",
|
|
656
|
-
"dwks19",
|
|
657
|
-
"unrecaptured_section_1250_gain",
|
|
658
|
-
]
|
|
659
|
-
]
|
|
660
|
-
form_6251_part_iii_required = np.any(
|
|
661
|
-
[
|
|
662
|
-
variable > 0
|
|
663
|
-
for variable in [
|
|
664
|
-
dwks10,
|
|
665
|
-
dwks13,
|
|
666
|
-
dwks14,
|
|
667
|
-
dwks19,
|
|
668
|
-
e24515,
|
|
669
|
-
]
|
|
670
|
-
]
|
|
671
|
-
)
|
|
672
|
-
|
|
673
|
-
# Complete Form 6251, Part III
|
|
674
|
-
line37 = dwks13
|
|
675
|
-
line38 = e24515
|
|
676
|
-
line39 = min_(line37 + line38, dwks10)
|
|
677
|
-
line40 = min_(
|
|
678
|
-
primary_reduced_income + secondary_reduced_income, line39
|
|
679
|
-
)
|
|
680
|
-
line41 = max_(
|
|
681
|
-
0, primary_reduced_income + secondary_reduced_income - line40
|
|
682
|
-
)
|
|
683
|
-
line42 = p.brackets.calc(line41)
|
|
684
|
-
line44 = dwks14
|
|
685
|
-
|
|
686
|
-
# Apply different thresholds for primary/secondary for capital gains
|
|
687
|
-
cg = parameters(period).gov.irs.capital_gains.brackets
|
|
688
|
-
primary_line45 = max_(
|
|
689
|
-
0, cg.thresholds["1"][filing_status] - line44
|
|
690
|
-
)
|
|
691
|
-
secondary_line45 = max_(0, cg.thresholds["1"]["SINGLE"] - line44)
|
|
692
|
-
|
|
693
|
-
line46 = min_(
|
|
694
|
-
primary_reduced_income + secondary_reduced_income, line37
|
|
695
|
-
)
|
|
696
|
-
primary_line47 = min_(primary_line45, line46)
|
|
697
|
-
secondary_line47 = min_(secondary_line45, line46)
|
|
698
|
-
|
|
699
|
-
cgtax1 = (
|
|
700
|
-
primary_line47 * cg.rates["1"]
|
|
701
|
-
+ secondary_line47 * cg.rates["1"]
|
|
702
|
-
)
|
|
703
|
-
|
|
704
|
-
line48 = line46 - (primary_line47 + secondary_line47)
|
|
705
|
-
line51 = dwks19
|
|
706
|
-
|
|
707
|
-
primary_line52 = primary_line45 + line51
|
|
708
|
-
secondary_line52 = secondary_line45 + line51
|
|
709
|
-
|
|
710
|
-
primary_line53 = max_(
|
|
711
|
-
0, cg.thresholds["2"][filing_status] - primary_line52
|
|
712
|
-
)
|
|
713
|
-
secondary_line53 = max_(
|
|
714
|
-
0, cg.thresholds["2"]["SINGLE"] - secondary_line52
|
|
715
|
-
)
|
|
716
|
-
|
|
717
|
-
primary_line54 = min_(line48, primary_line53)
|
|
718
|
-
secondary_line54 = min_(line48, secondary_line53)
|
|
719
|
-
|
|
720
|
-
cgtax2 = (
|
|
721
|
-
primary_line54 * cg.rates["2"]
|
|
722
|
-
+ secondary_line54 * cg.rates["2"]
|
|
723
|
-
)
|
|
724
|
-
|
|
725
|
-
line56 = (
|
|
726
|
-
primary_line47
|
|
727
|
-
+ secondary_line47
|
|
728
|
-
+ primary_line54
|
|
729
|
-
+ secondary_line54
|
|
730
|
-
)
|
|
731
|
-
line57 = where(line41 == line56, 0, line46 - line56)
|
|
732
|
-
linex2 = where(
|
|
733
|
-
line41 == line56,
|
|
734
|
-
0,
|
|
735
|
-
max_(0, primary_line54 + secondary_line54 - line48),
|
|
736
|
-
)
|
|
737
|
-
cgtax3 = line57 * cg.rates["3"]
|
|
738
|
-
|
|
739
|
-
line61 = where(
|
|
740
|
-
line38 == 0,
|
|
741
|
-
0,
|
|
742
|
-
p.capital_gains.capital_gain_excess_tax_rate
|
|
743
|
-
* max_(
|
|
744
|
-
0,
|
|
745
|
-
(
|
|
746
|
-
primary_reduced_income
|
|
747
|
-
+ secondary_reduced_income
|
|
748
|
-
- line41
|
|
749
|
-
- line56
|
|
750
|
-
- line57
|
|
751
|
-
- linex2
|
|
752
|
-
),
|
|
753
|
-
),
|
|
754
|
-
)
|
|
755
|
-
line62 = line42 + cgtax1 + cgtax2 + cgtax3 + line61
|
|
756
|
-
line64 = min_(reduced_income_tax, line62)
|
|
757
|
-
line31 = where(
|
|
758
|
-
form_6251_part_iii_required, line64, reduced_income_tax
|
|
759
|
-
)
|
|
760
|
-
|
|
761
|
-
# Form 6251, Part II bottom
|
|
762
|
-
is_joint = tax_unit("tax_unit_is_joint", period)
|
|
763
|
-
divisor = where(is_joint, 2, 1)
|
|
764
|
-
line32 = tax_unit("foreign_tax_credit", period) / divisor
|
|
765
|
-
line33 = line31 - line32
|
|
766
|
-
regular_tax_before_credits = (
|
|
767
|
-
tax_unit("regular_tax_before_credits", period) / divisor
|
|
768
|
-
)
|
|
769
|
-
lump_sum_distributions = (
|
|
770
|
-
tax_unit("form_4972_lumpsum_distributions", period) / divisor
|
|
771
|
-
)
|
|
772
|
-
capital_gains = tax_unit("capital_gains_tax", period)
|
|
773
|
-
tax_before_credits = regular_tax_before_credits + capital_gains
|
|
774
|
-
|
|
775
|
-
return tax_unit.sum(
|
|
776
|
-
max_(
|
|
777
|
-
0,
|
|
778
|
-
line33
|
|
779
|
-
- max_(
|
|
780
|
-
0,
|
|
781
|
-
(tax_before_credits - line32 - lump_sum_distributions),
|
|
782
|
-
),
|
|
783
|
-
)
|
|
784
|
-
)
|
|
785
|
-
|
|
786
|
-
class reform(Reform):
|
|
787
|
-
def apply(self):
|
|
788
|
-
self.update_variable(taxable_income_person)
|
|
789
|
-
self.update_variable(income_tax_main_rates)
|
|
790
|
-
self.update_variable(basic_standard_deduction_person)
|
|
791
|
-
self.update_variable(standard_deduction_person)
|
|
792
|
-
self.update_variable(taxable_income_deductions_person)
|
|
793
|
-
self.update_variable(is_primary_earner)
|
|
794
|
-
self.update_variable(capital_gains_tax)
|
|
795
|
-
self.update_variable(net_capital_gain_person)
|
|
796
|
-
self.update_variable(adjusted_net_capital_gain_person)
|
|
797
|
-
self.update_variable(alternative_minimum_tax)
|
|
798
|
-
self.update_variable(amt_income_person)
|
|
799
|
-
self.update_variable(amt_excluded_deductions_person)
|
|
800
|
-
self.update_variable(bonus_guaranteed_deduction_person)
|
|
801
|
-
self.update_variable(additional_standard_deduction_person)
|
|
802
|
-
|
|
803
|
-
return reform
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
def create_second_earner_tax_reform(parameters, period, bypass: bool = False):
|
|
807
|
-
if bypass:
|
|
808
|
-
return create_second_earner_tax()
|
|
809
|
-
|
|
810
|
-
p = parameters(period).gov.contrib.second_earner_reform
|
|
811
|
-
|
|
812
|
-
if p.in_effect:
|
|
813
|
-
return create_second_earner_tax()
|
|
814
|
-
else:
|
|
815
|
-
return None
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
second_earner_tax_reform = create_second_earner_tax_reform(
|
|
819
|
-
None, None, bypass=True
|
|
820
|
-
)
|