From 3eb17b3ce016323fd0fdca313ab669aa3da02963 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Fri, 12 Jun 2026 16:48:04 -0400 Subject: [PATCH 1/2] Add test of 2022 EITC/ACTC credit results --- tests/expected_taxcalc_results_2022.yaml | 23 ++++++ tests/test_taxcalc_results_2022.py | 95 ++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 tests/expected_taxcalc_results_2022.yaml create mode 100644 tests/test_taxcalc_results_2022.py diff --git a/tests/expected_taxcalc_results_2022.yaml b/tests/expected_taxcalc_results_2022.yaml new file mode 100644 index 00000000..61f7c7c5 --- /dev/null +++ b/tests/expected_taxcalc_results_2022.yaml @@ -0,0 +1,23 @@ +# Expected 2022 EITC and ACTC aggregates from IRS Statistics of Income +# Publication 1304 (2022), Table A. +# +# DATA SOURCE +# Internal Revenue Service, "Statistics of Income — 2022 Individual +# Income Tax Returns," Publication 1304, Table A — "Selected Income +# and Tax Items, by Size and Accumulated Size of Adjusted Gross Income" +# +# DEFINITIONS +# n_returns_mil : Number of returns with a positive credit amount (millions). +# amount_bil : Total credit amount (billions of dollars). +# +# NOTES +# EITC = Earned Income Credit (refundable); taxcalc variable "eitc". +# ACTC = Additional Child Tax Credit (refundable); taxcalc variable "c11070". + +eitc: + n_returns_mil: 24.09 + amount_bil: 60.07 + +actc: + n_returns_mil: 18.08 + amount_bil: 34.84 diff --git a/tests/test_taxcalc_results_2022.py b/tests/test_taxcalc_results_2022.py new file mode 100644 index 00000000..eafa6f75 --- /dev/null +++ b/tests/test_taxcalc_results_2022.py @@ -0,0 +1,95 @@ +""" +Test that 2022 Tax-Calculator results for EITC and ACTC match +IRS Statistics of Income (SOI) Publication 1304 aggregates. + +Comparisons (all tax units, year 2022): + - Earned Income Credit (EITC): count (millions) and amount (billions) + - Additional Child Tax Credit (ACTC): count (millions) and amount (billions) +""" + +import yaml +import pytest +import taxcalc + +from tmd.imputation_assumptions import TAXYEAR, SOI_IITAX_SPEC, CREDIT_CLAIMING + +OLD_CREDIT_CLAIMING = False +CLAIM_PROB_SCALE = { + "eitc": 1.04, + "actc": 1.57, +} +NEW_CREDIT_CLAIMING = { + "eitc_claim_prob_scale": {"2022": CLAIM_PROB_SCALE["eitc"]}, + "actc_claim_prob_scale": {"2022": CLAIM_PROB_SCALE["actc"]}, +} +MAX_RELATIVE_TOLERANCE = { + "n_returns_mil": 0.07, + "amount_bil": 0.002, +} + + +@pytest.mark.skipif( + TAXYEAR != 2022, + reason="expected values are calibrated to TAXYEAR=2022", +) +def test_taxcalc_results_2022( + tests_folder, tmd_variables, tmd_weights_path, tmd_growfactors_path +): + epath = tests_folder / "expected_taxcalc_results_2022.yaml" + with open(epath, "r", encoding="utf-8") as f: + exp = yaml.safe_load(f) + + pol = taxcalc.Policy() + pol.implement_reform(SOI_IITAX_SPEC) + if OLD_CREDIT_CLAIMING: + pol.implement_reform(CREDIT_CLAIMING) + else: + pol.implement_reform(NEW_CREDIT_CLAIMING) + recs = taxcalc.Records( + data=tmd_variables, + start_year=TAXYEAR, + weights=str(tmd_weights_path), + gfactors=taxcalc.GrowFactors( + growfactors_filename=str(tmd_growfactors_path) + ), + adjust_ratios=None, + exact_calculations=True, + weights_scale=1.0, + ) + sim = taxcalc.Calculator(policy=pol, records=recs) + sim.advance_to_year(TAXYEAR) + sim.calc_all() + + wt = sim.array("s006") + eitc = sim.array("eitc") + actc = sim.array("c11070") + + actual = { + "eitc": { + "n_returns_mil": float((wt * (eitc > 0)).sum() * 1e-6), + "amount_bil": float((wt * eitc).sum() * 1e-9), + }, + "actc": { + "n_returns_mil": float((wt * (actc > 0)).sum() * 1e-6), + "amount_bil": float((wt * actc).sum() * 1e-9), + }, + } + + errors = [] + for credit in ("eitc", "actc"): + for metric in ("n_returns_mil", "amount_bil"): + act = actual[credit][metric] + ex = exp[credit][metric] + rel_diff = act / ex - 1.0 + if abs(rel_diff) >= MAX_RELATIVE_TOLERANCE[metric]: + errors.append( + f"{credit}.{metric}: " + f"act={act:.2f} exp={ex:.2f} " + f"rel_diff={rel_diff:+.4f} " + f"tol={MAX_RELATIVE_TOLERANCE[metric]:.2f}" + ) + + if errors: + raise ValueError( + "\nACT-vs-EXP TAXCALC 2022 DIFFERENCES:\n" + "\n".join(errors) + ) From c1f007b0084d542057469f9728b9d80af344aee6 Mon Sep 17 00:00:00 2001 From: martinholmer Date: Sat, 13 Jun 2026 17:27:49 -0400 Subject: [PATCH 2/2] Cosmetic changes to new test code --- tests/test_taxcalc_results_2022.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_taxcalc_results_2022.py b/tests/test_taxcalc_results_2022.py index eafa6f75..7634c95e 100644 --- a/tests/test_taxcalc_results_2022.py +++ b/tests/test_taxcalc_results_2022.py @@ -37,7 +37,7 @@ def test_taxcalc_results_2022( ): epath = tests_folder / "expected_taxcalc_results_2022.yaml" with open(epath, "r", encoding="utf-8") as f: - exp = yaml.safe_load(f) + expect = yaml.safe_load(f) pol = taxcalc.Policy() pol.implement_reform(SOI_IITAX_SPEC) @@ -79,14 +79,14 @@ def test_taxcalc_results_2022( for credit in ("eitc", "actc"): for metric in ("n_returns_mil", "amount_bil"): act = actual[credit][metric] - ex = exp[credit][metric] - rel_diff = act / ex - 1.0 + exp = expect[credit][metric] + rel_diff = act / exp - 1.0 if abs(rel_diff) >= MAX_RELATIVE_TOLERANCE[metric]: errors.append( f"{credit}.{metric}: " - f"act={act:.2f} exp={ex:.2f} " + f"act={act:.2f} exp={exp:.2f} " f"rel_diff={rel_diff:+.4f} " - f"tol={MAX_RELATIVE_TOLERANCE[metric]:.2f}" + f"tol={MAX_RELATIVE_TOLERANCE[metric]:.4f}" ) if errors: