diff --git a/us/crfb/agi_surtax/agi_surtax_10yr_impact.csv b/us/crfb/agi_surtax/agi_surtax_10yr_impact.csv index 1b65b42..520d307 100644 --- a/us/crfb/agi_surtax/agi_surtax_10yr_impact.csv +++ b/us/crfb/agi_surtax/agi_surtax_10yr_impact.csv @@ -1,12 +1,12 @@ year,revenue_billions -2026,64.72 -2027,68.62 -2028,72.74 -2029,77.51 -2030,82.99 -2031,88.99 -2032,95.46 -2033,102.23 -2034,109.78 -2035,117.7 -Total,880.74 +2026,65.92 +2027,69.83 +2028,74.09 +2029,78.98 +2030,84.53 +2031,90.58 +2032,97.18 +2033,104.1 +2034,111.78 +2035,119.81 +Total,896.79 diff --git a/us/crfb/agi_surtax/agi_surtax_expanded_base_10yr_impact.csv b/us/crfb/agi_surtax/agi_surtax_expanded_base_10yr_impact.csv index 278bf6f..518ac5e 100644 --- a/us/crfb/agi_surtax/agi_surtax_expanded_base_10yr_impact.csv +++ b/us/crfb/agi_surtax/agi_surtax_expanded_base_10yr_impact.csv @@ -1,12 +1,12 @@ year,revenue_billions -2026,76.81 -2027,81.25 -2028,85.67 -2029,91.08 -2030,97.18 -2031,103.8 -2032,110.89 -2033,118.33 -2034,126.65 -2035,135.49 -Total,1027.15 +2026,77.11 +2027,81.84 +2028,86.85 +2029,92.62 +2030,98.69 +2031,105.57 +2032,112.9 +2033,120.58 +2034,128.84 +2035,137.6 +Total,1042.6 diff --git a/us/crfb/agi_surtax/agi_surtax_impact.ipynb b/us/crfb/agi_surtax/agi_surtax_impact.ipynb deleted file mode 100644 index 8367cea..0000000 --- a/us/crfb/agi_surtax/agi_surtax_impact.ipynb +++ /dev/null @@ -1,184 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "imports", - "metadata": {}, - "outputs": [], - "source": [ - "from policyengine_us import Microsimulation\n", - "from policyengine_core.reforms import Reform\n", - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "c52529ba", - "metadata": {}, - "outputs": [], - "source": [ - "def run_10yr_impact(reform, csv_path, label=\"\"):\n", - " \"\"\"Run 10-year income tax impact analysis for a reform.\"\"\"\n", - " pd.DataFrame(columns=[\"year\", \"revenue_billions\"]).to_csv(csv_path, index=False)\n", - " \n", - " total = 0\n", - " for year in range(2026, 2036):\n", - " baseline = Microsimulation()\n", - " reformed = Microsimulation(reform=reform)\n", - " \n", - " baseline_tax = baseline.calculate(\"income_tax\", period=year).sum() / 1e9\n", - " reformed_tax = reformed.calculate(\"income_tax\", period=year).sum() / 1e9\n", - " \n", - " revenue = reformed_tax - baseline_tax\n", - " total += revenue\n", - " print(f\"{year}: ${revenue:.2f}B\")\n", - " \n", - " pd.DataFrame([{\"year\": year, \"revenue_billions\": round(revenue, 2)}]).to_csv(\n", - " csv_path, mode=\"a\", header=False, index=False\n", - " )\n", - " \n", - " pd.DataFrame([{\"year\": \"Total\", \"revenue_billions\": round(total, 2)}]).to_csv(\n", - " csv_path, mode=\"a\", header=False, index=False\n", - " )\n", - " print(f\"\\n10-Year Total{' (' + label + ')' if label else ''}: ${total:.2f}B\")\n", - " return total" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "analysis", - "metadata": {}, - "outputs": [], - "source": [ - "# Basic AGI surtax (1% on $100k/$200k, 2% on $1M)\n", - "reform_basic = Reform.from_dict({\n", - " \"gov.contrib.crfb.surtax.in_effect\": {\n", - " \"2026-01-01.2100-12-31\": True\n", - " },\n", - "}, country_id=\"us\")\n", - "\n", - "run_10yr_impact(reform_basic, \"agi_surtax_10yr_impact.csv\", \"Basic\")" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "u9d2r5sr7xh", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2026: $76.81B\n", - "2027: $81.25B\n", - "2028: $85.67B\n", - "2029: $91.08B\n", - "2030: $97.18B\n", - "2031: $103.80B\n", - "2032: $110.89B\n", - "2033: $118.33B\n", - "2034: $126.65B\n", - "2035: $135.49B\n", - "\n", - "10-Year Total (Expanded Base): $1027.15B\n" - ] - }, - { - "data": { - "text/plain": [ - "np.float64(1027.1456010329425)" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Surtax with EXPANDED BASE (adds retirement contributions, HSA, tax-exempt income, etc.)\n", - "reform_expanded = Reform.from_dict({\n", - " \"gov.contrib.crfb.surtax.in_effect\": {\n", - " \"2026-01-01.2100-12-31\": True\n", - " },\n", - " \"gov.contrib.crfb.surtax.increased_base.in_effect\": {\n", - " \"2026-01-01.2100-12-31\": True\n", - " },\n", - "}, country_id=\"us\")\n", - "\n", - "run_10yr_impact(reform_expanded, \"agi_surtax_expanded_base_10yr_impact.csv\", \"Expanded Base\")" - ] - }, - { - "cell_type": "code", - "id": "104a0qlmca5", - "source": "# CBO Option 46b comparison: 2% flat surtax on AGI above $100k/$200k\nreform_cbo_2pct = Reform.from_dict({\n \"gov.contrib.crfb.surtax.in_effect\": {\n \"2026-01-01.2100-12-31\": True\n },\n # Override to flat 2% (no tiered structure)\n \"gov.contrib.crfb.surtax.rate.single[1].rate\": {\n \"2026-01-01.2100-12-31\": 0.02\n },\n \"gov.contrib.crfb.surtax.rate.single[2].rate\": {\n \"2026-01-01.2100-12-31\": 0.02\n },\n \"gov.contrib.crfb.surtax.rate.joint[1].rate\": {\n \"2026-01-01.2100-12-31\": 0.02\n },\n \"gov.contrib.crfb.surtax.rate.joint[2].rate\": {\n \"2026-01-01.2100-12-31\": 0.02\n },\n}, country_id=\"us\")\n\nrun_10yr_impact(reform_cbo_2pct, \"cbo_comparison_2pct_10yr.csv\", \"CBO 2% Flat\")", - "metadata": {}, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "c5f2c0e5", - "metadata": {}, - "outputs": [], - "source": [ - "baseline = Microsimulation()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "5fd22e28", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "np.float64(0.0)" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "baseline.calculate(\"traditional_403b_contributions\", 2026).sum()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3ebe1e92", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file diff --git a/us/crfb/agi_surtax/agi_surtax_impact.py b/us/crfb/agi_surtax/agi_surtax_impact.py new file mode 100644 index 0000000..9580c4e --- /dev/null +++ b/us/crfb/agi_surtax/agi_surtax_impact.py @@ -0,0 +1,189 @@ +from policyengine_us import Microsimulation +from policyengine_core.reforms import Reform +import pandas as pd + + +def run_10yr_impact(reform, csv_path, label=""): + """Run 10-year income tax impact analysis for a reform.""" + pd.DataFrame(columns=["year", "revenue_billions"]).to_csv(csv_path, index=False) + + total = 0 + for year in range(2026, 2036): + baseline = Microsimulation() + reformed = Microsimulation(reform=reform) + + baseline_tax = baseline.calculate("income_tax", period=year).sum() / 1e9 + reformed_tax = reformed.calculate("income_tax", period=year).sum() / 1e9 + + revenue = reformed_tax - baseline_tax + total += revenue + print(f"{year}: ${revenue:.2f}B") + + pd.DataFrame([{"year": year, "revenue_billions": round(revenue, 2)}]).to_csv( + csv_path, mode="a", header=False, index=False + ) + + pd.DataFrame([{"year": "Total", "revenue_billions": round(total, 2)}]).to_csv( + csv_path, mode="a", header=False, index=False + ) + print(f"\n10-Year Total{' (' + label + ')' if label else ''}: ${total:.2f}B") + return total + + +def validate_income_sources(year=2026): + """Validate that key income sources have data in the microsimulation.""" + print(f"\n{'=' * 80}") + print(f"Income Source Validation ({year})") + print("=" * 80) + + baseline = Microsimulation() + + # Sources with external benchmarks for validation + # Format: (variable_name, label, external_low, external_high, source_note) + sources = [ + ( + "traditional_401k_contributions", + "Traditional 401(k) contributions", + 250, + 300, + "JCT JCX-48-24: $251B tax expenditure implies ~$300-400B contributions", + ), + ( + "tax_exempt_social_security", + "Tax-exempt Social Security", + 580, + 690, + "SSA: $1.38T total benefits (2023), ~42-50% tax-exempt", + ), + ( + "health_insurance_premiums", + "Employer health insurance premiums", + 900, + 1000, + "CMS/KFF: $1.6T private insurance, ~75% employer-paid", + ), + ( + "tax_exempt_interest_income", + "Tax-exempt interest income", + 120, + 150, + "CRS IF12969: $4.2T muni bonds at 3-4% yield", + ), + ( + "health_savings_account_ald", + "Health Savings Account (ALD)", + 50, + 60, + "Devenir 2024: $56B total HSA contributions", + ), + ( + "traditional_ira_contributions", + "Traditional IRA contributions", + 20, + 25, + "ICI: $22B traditional IRA contributions (2020)", + ), + ( + "traditional_403b_contributions", + "Traditional 403(b) contributions", + 50, + 70, + "Expected: similar to 401k scale for nonprofits/education", + ), + ] + + results = [] + print( + f"{'Income Source':<35} {'PE 2026':>12} {'External':>15} {'Status':>10}" + ) + print("-" * 80) + + for var_name, label, ext_low, ext_high, note in sources: + try: + total = baseline.calculate(var_name, period=year).sum() / 1e9 + external_range = f"${ext_low}-{ext_high}B" + + if total == 0: + status = "⚠️ No data" + elif ext_low <= total <= ext_high: + status = "✅ Match" + elif total < ext_low * 0.5: + status = "❌ Low" + elif total > ext_high * 1.5: + status = "❌ High" + else: + status = "⚠️ Close" + + print(f"{label:<35} {f'${total:.1f}B':>12} {external_range:>15} {status:>10}") + results.append((label, total, ext_low, ext_high, status, note)) + except Exception as e: + print(f"{label:<35} {'N/A':>12} {f'${ext_low}-{ext_high}B':>15} {'❌ Error':>10}") + results.append((label, None, ext_low, ext_high, f"Error: {e}", note)) + + print("-" * 80) + print("\nNotes on external benchmarks:") + for label, total, ext_low, ext_high, status, note in results: + if "❌" in status or "⚠️" in status: + print(f" • {label}: {note}") + + return results + + +if __name__ == "__main__": + # Validate income sources first + validate_income_sources(2026) + + # Basic AGI surtax (1% on $100k/$200k, 2% on $1M) + reform_basic = Reform.from_dict( + { + "gov.contrib.crfb.surtax.in_effect": {"2026-01-01.2100-12-31": True}, + }, + country_id="us", + ) + + print("=" * 50) + print("Basic AGI Surtax") + print("=" * 50) + run_10yr_impact(reform_basic, "agi_surtax_10yr_impact.csv", "Basic") + + # Surtax with EXPANDED BASE (adds retirement contributions, HSA, tax-exempt income, etc.) + reform_expanded = Reform.from_dict( + { + "gov.contrib.crfb.surtax.in_effect": {"2026-01-01.2100-12-31": True}, + "gov.contrib.crfb.surtax.increased_base.in_effect": { + "2026-01-01.2100-12-31": True + }, + }, + country_id="us", + ) + + print("\n" + "=" * 50) + print("Expanded Base AGI Surtax") + print("=" * 50) + run_10yr_impact(reform_expanded, "agi_surtax_expanded_base_10yr_impact.csv", "Expanded Base") + + # CBO Option 46b comparison: 2% flat surtax on AGI above $100k/$200k + reform_cbo_2pct = Reform.from_dict( + { + "gov.contrib.crfb.surtax.in_effect": {"2026-01-01.2100-12-31": True}, + # Override to flat 2% (no tiered structure) + "gov.contrib.crfb.surtax.rate.single[1].rate": { + "2026-01-01.2100-12-31": 0.02 + }, + "gov.contrib.crfb.surtax.rate.single[2].rate": { + "2026-01-01.2100-12-31": 0.02 + }, + "gov.contrib.crfb.surtax.rate.joint[1].rate": { + "2026-01-01.2100-12-31": 0.02 + }, + "gov.contrib.crfb.surtax.rate.joint[2].rate": { + "2026-01-01.2100-12-31": 0.02 + }, + }, + country_id="us", + ) + + print("\n" + "=" * 50) + print("CBO 2% Flat Surtax Comparison") + print("=" * 50) + run_10yr_impact(reform_cbo_2pct, "cbo_comparison_2pct_10yr.csv", "CBO 2% Flat") diff --git a/us/crfb/agi_surtax/cbo_comparison_2pct_10yr.csv b/us/crfb/agi_surtax/cbo_comparison_2pct_10yr.csv index 4667862..0826c62 100644 --- a/us/crfb/agi_surtax/cbo_comparison_2pct_10yr.csv +++ b/us/crfb/agi_surtax/cbo_comparison_2pct_10yr.csv @@ -1,12 +1,12 @@ year,revenue_billions -2026,104.6 -2027,111.28 -2028,118.21 -2029,125.98 -2030,134.67 -2031,144.06 -2032,154.16 -2033,164.76 -2034,176.55 -2035,188.89 -Total,1423.14 +2026,105.0 +2027,111.42 +2028,118.25 +2029,125.92 +2030,134.44 +2031,143.67 +2032,153.82 +2033,164.5 +2034,176.32 +2035,188.69 +Total,1422.03