-
-
Notifications
You must be signed in to change notification settings - Fork 150
/
recipe06_py.html
174 lines (161 loc) · 7.78 KB
/
recipe06_py.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
<title>recipe06.py</title><pre>
"""
Recipe that illustrates how to customize the taxcalc Calculator class so that
it can analyze a non-parametric reform (that is, a reform that cannot be
represented using existing taxcalc Policy parameters.
The technique for doing this customization is standard in object-oriented
programming: a child class is derived from a parent class and then customized.
The derived child class inherits all the data and methods of the parent class,
but can be customized by adding new data and methods or by overriding inherited
methods.
The reform used to illustrate this programming technique is somewhat like
the Cost-of-Living Refund, a refundable credit that was being discussed in
tax policy circles during 2019 as a replacement for the EITC. But the
reform analyzed here is not exactly like the Cost-of-Living Refund, so we
call it a pseudo cost-of-living refund to emphasize that it is not meant
to be an accurate representation of the Cost-of-Living Refund proposal.
"""
import numpy as np
import taxcalc as tc
class Calculator(tc.Calculator):
"""
Customized Calculator class that inherits all tc.Calculator data and
methods, adding or overriding some to get the desired customization.
"""
def __init__(self, policy=None, records=None, verbose=False,
sync_years=True, consumption=None,
# above are the tc.Calculator constructor arguments
# below is new constructor argument used in customization
colr_active=False):
# call the parent class constructor
super().__init__(policy=policy, records=records,
verbose=verbose, sync_years=sync_years,
consumption=consumption)
# remember whether pseudo_COLR policy is active or not
self.colr_active = colr_active
# declare colr_param dictionary that will contain pseudo COLR policy
self.colr_param = dict()
def specify_pseudo_colr_policy(self):
"""
Specify policy parameters for the COLR policy in the current_year.
See use of these parameters below in the pseudo_colr_amount method.
"""
# reform implementation year
reform_year = 2020
# specify dictionary of parameter names and values for reform_year
self.colr_param = {
# credit phase-in rate on earnings
'COLR_rt': 1.0,
# ceiling on refundable credit varies by filing-unit type, MARS
'COLR_c': np.array([4000, 8000, 4000, 4000, 4000],
dtype=np.float64),
# credit phase-out start AGI level varies by filing-unit type, MARS
'COLR_ps': np.array([30000, 50000, 30000, 30000, 30000],
dtype=np.float64),
# credit phase-out rate per dollar of AGI above COLR_ps level
'COLR_prt': 0.2
}
# set pseudo COLR parameter values for current year
this_year = self.current_year
if self.colr_active and this_year >= reform_year:
# set inflation-indexed values of COLR_c and COLR_ps for this year
irates = self.__policy.inflation_rates()
syr = tc.Policy.JSON_START_YEAR
for name in ['COLR_c', 'COLR_ps']:
value = self.colr_param[name]
for year in range(reform_year, this_year):
value *= (1.0 + irates[year - syr])
self.colr_param[name] = np.round(value, 2) # to nearest penny
else: # if policy not active or if this year is before the reform year
# set ceiling to zero
self.colr_param['COLR_c'] = np.array([0.0, 0.0, 0.0, 0.0, 0.0],
dtype=np.float64)
tracing = False # set to True to see parameter values for this year
if tracing:
for name in self.colr_param:
print('> {} {} {}'.format(
this_year, name, self.colr_param[name]
))
def pseudo_colr_amount(self):
"""
Calculate pseudo Cost-of-Living Refund amount.
Note this is simply meant to illustrate a Python programming technique;
this function does NOT calculate an exact Cost-of-Living Refund amount.
See setting of parameters above in specify_pseudo_COLR_policy method.
"""
recs = self.__records
# create MARS-specific policy parameter arrays
mars_indicators = [recs.MARS == 1, recs.MARS == 2, recs.MARS == 3,
recs.MARS == 4, recs.MARS == 5]
colr_c = np.select(mars_indicators, self.colr_param['COLR_c'])
colr_ps = np.select(mars_indicators, self.colr_param['COLR_ps'])
colr_rt = self.colr_param['COLR_rt']
colr_prt = self.colr_param['COLR_prt']
# compute colr_amt
amt_pre_phaseout = np.minimum(recs.e00200 * colr_rt, colr_c)
phaseout = np.maximum((recs.c00100 - colr_ps) * colr_prt, 0.)
colr_amt = np.maximum(amt_pre_phaseout - phaseout, 0.)
setattr(recs, 'colr_amount', colr_amt)
# reduce income and combined taxes because COLR is a refundable credit
recs.iitax -= colr_amt
recs.combined -= colr_amt
# delete local arrays used only in this method
del mars_indicators
del colr_c
del colr_ps
del amt_pre_phaseout
del phaseout
del colr_amt
def calc_all(self, zero_out_calc_vars=False):
"""
Call all tax-calculation functions for the current_year.
"""
tc.BenefitPrograms(self)
self._calc_one_year(zero_out_calc_vars)
tc.BenefitSurtax(self)
tc.BenefitLimitation(self)
tc.FairShareTax(self.__policy, self.__records)
tc.LumpSumTax(self.__policy, self.__records)
# specify new method to set pseudo COLR policy parameters
self.specify_pseudo_colr_policy() # (see above)
# call new method to calculate pseudo COLR amount
self.pseudo_colr_amount() # (see above)
tc.ExpandIncome(self.__policy, self.__records)
tc.AfterTaxIncome(self.__policy, self.__records)
# end of customized Calculator class definition
# top-level logic of program that uses customized Calculator class
policy1 = tc.Policy() # baseline policy is current-law policy
policy2 = tc.Policy() # parametric reform, reformC.json, eliminates EITC
policy2.implement_reform(tc.Policy.read_json_reform('reformC.json'))
# specify customized Calculator objects for baseline and reform:
# baseline calc1 uses policy1 (current-law) and colr_active=False
# reform calc2 uses policy2 (no EITC) and colr_active=True
cps_records = tc.Records.cps_constructor()
calc1 = Calculator(policy=policy1, records=cps_records, colr_active=False)
calc2 = Calculator(policy=policy2, records=cps_records, colr_active=True)
# calculate tax liabilities for years around the reform year
cyr_first = 2019
cyr_last = 2022
for cyr in range(cyr_first, cyr_last + 1):
# advance to and calculate for specified cyr
calc1.advance_to_year(cyr)
calc1.calc_all()
calc2.advance_to_year(cyr)
calc2.calc_all()
# tabulate weighted amounts
funits = calc1.total_weight()
itax1 = calc1.weighted_total('iitax')
itax2 = calc2.weighted_total('iitax')
eitc1 = calc1.weighted_total('eitc')
eitc2 = calc2.weighted_total('eitc')
colr1 = calc1.weighted_total('colr_amount')
colr2 = calc2.weighted_total('colr_amount')
# print weighted amounts for cyr
if cyr == cyr_first:
print('YEAR UNITS ITAX1 ITAX2 EITC1 EITC2 COLR1 COLR2')
line = '{} {:.1f} {:.1f} {:.1f} {:5.1f} {:5.1f} {:5.1f} {:5.1f}'
print(line.format(cyr, funits * 1e-6,
itax1 * 1e-9, itax2 * 1e-9,
eitc1 * 1e-9, eitc2 * 1e-9,
colr1 * 1e-9, colr2 * 1e-9))
</pre>