New York EITC#

The New York EITC equals 30 percent of the federal EITC, minus the New York household credit.

Examples#

The New York EITC provided up to $2,000 in 2021, for a joint filer with three children and earnings between about $25,200 and $26,100. It is not smooth because it subtracts the New York household credit, which falls with income in a stepped manner.

from policyengine_us import IndividualSim, Microsimulation
import pandas as pd
import plotly.express as px


LIGHT_GRAY = "#F5F5F5"
GRAY = "#BDBDBD"
BLUE = "#5091cc"
LIGHT_BLUE = "lightblue"
DARK_BLUE = "darkblue"

COLOR_MAP = {"0": GRAY, "1": LIGHT_BLUE, "2": BLUE, "3": DARK_BLUE}


def make_eitc(adults, children):
    sim = IndividualSim(year=2021)
    sim.add_person(name="head", age=25)
    members = ["head"]
    if adults == 2:
        sim.add_person(name="spouse")
        members += ["spouse"]
    for i in range(children):
        child = "child{}".format(i)
        sim.add_person(name=child, age=6)
        members += [child]
    sim.add_tax_unit(name="tax_unit", members=members)
    sim.add_spm_unit(name="spm_unit", members=members)
    sim.add_household(name="household", members=members, state_code="NY")
    sim.vary("employment_income", max=60_000, step=100)
    return pd.DataFrame(
        dict(
            employment_income=sim.calc("employment_income")[0],
            ny_eitc=sim.calc("ny_eitc")[0].round(),
            mtr=-sim.deriv("ny_eitc", "employment_income", wrt_target="head"),
            adults=adults,
            children=str(children),
        )
    )


# Make a table of EITCs for different numbers of adults and children.
l = []
for adults in range(1, 3):
    for children in range(0, 4):
        l.append(make_eitc(adults, children))

df = pd.concat(l)

LABELS = dict(
    employment_income="Employment income",
    mtr="Marginal tax rate of NY EITC",
    adults="Adults",
    children="Children",
    ny_eitc="New York EITC",
)

fig = px.line(
    df,
    "employment_income",
    "ny_eitc",
    color="children",
    animation_frame="adults",
    labels=LABELS,
    title="New York Earned Income Tax Credit",
    color_discrete_map=COLOR_MAP,
)
fig.update_layout(
    xaxis_tickformat="$,",
    yaxis_tickformat="$,",
    plot_bgcolor="white",
    xaxis_gridcolor=LIGHT_GRAY,
    yaxis_gridcolor=LIGHT_GRAY,
)
fig.show()

The NY EITC creates marginal tax rates ranging from -13.5 percent to +6.3 percent, plus downward spikes corresponding to the NY household credit cliffs.

fig = px.line(
    df,
    "employment_income",
    "mtr",
    color="children",
    animation_frame="adults",
    labels=LABELS,
    title="NY EITC marginal tax rate",
    color_discrete_map=COLOR_MAP,
)
fig.update_layout(
    xaxis_tickformat="$,",
    yaxis_tickformat=".1%",
    plot_bgcolor="white",
    xaxis_gridcolor=LIGHT_GRAY,
    yaxis_gridcolor=LIGHT_GRAY,
)
fig.show()

Budgetary impact#

Applying the New York EITC logic to the 2020 Current Population Survey March Supplement shows that the program cost an estimated $533 million in 2020.

from policyengine_us.model_api import *


def budgetary_impacts(
    variable: str, data_year: int = 2020, policy_year: int = 2022
) -> Tuple:
    """Calculates the budgetary impact of a variable for a given policy year.

    Args:
        variable (str): The variable to calculate the budgetary impact of.
        data_year (int): The year of the data to use.
        policy_year (int): The year of the policy to use.

    Returns:
        Tuple: A tuple of the total cost and budgetary impact of repealing the variable,
        respectively, in millions.
    """

    class ignore_reported(Reform):
        def apply(self):
            self.neutralize_variable("spm_unit_net_income_reported")

    class neutralize(Reform):
        def apply(self):
            self.neutralize_variable(variable)

    sim = Microsimulation(ignore_reported, year=data_year)
    sim_neutralized = Microsimulation(
        (ignore_reported, neutralize), year=data_year
    )
    program_value = sim.calc(variable, period=policy_year).sum()
    baseline_net_income = sim.calc(
        "spm_unit_net_income", period=policy_year
    ).sum()
    neutralized_net_income = sim_neutralized.calc(
        "spm_unit_net_income", period=policy_year
    ).sum()
    budgetary_impact = neutralized_net_income - baseline_net_income
    return round(program_value / 1e6), round(budgetary_impact / 1e6)


budgetary_impacts("ny_eitc")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 42
     38     budgetary_impact = neutralized_net_income - baseline_net_income
     39     return round(program_value / 1e6), round(budgetary_impact / 1e6)
---> 42 budgetary_impacts("ny_eitc")

Cell In[3], line 27, in budgetary_impacts(variable, data_year, policy_year)
     24     def apply(self):
     25         self.neutralize_variable(variable)
---> 27 sim = Microsimulation(ignore_reported, year=data_year)
     28 sim_neutralized = Microsimulation(
     29     (ignore_reported, neutralize), year=data_year
     30 )
     31 program_value = sim.calc(variable, period=policy_year).sum()

File ~/work/policyengine-us/policyengine-us/policyengine_us/system.py:115, in Microsimulation.__init__(self, *args, **kwargs)
    114 def __init__(self, *args, **kwargs):
--> 115     super().__init__(*args, **kwargs)
    117     reform = create_structural_reforms_from_parameters(
    118         self.tax_benefit_system.parameters, year_start
    119     )
    120     if reform is not None:

TypeError: __init__() got an unexpected keyword argument 'year'