Skip to content

Add TAXSIM-compatible marginal tax rates (frate, srate, ficar)#770

Merged
PavelMakarchuk merged 9 commits intomainfrom
marginal-rate-comparison
Mar 19, 2026
Merged

Add TAXSIM-compatible marginal tax rates (frate, srate, ficar)#770
PavelMakarchuk merged 9 commits intomainfrom
marginal-rate-comparison

Conversation

@PavelMakarchuk
Copy link
Collaborator

Summary

Implements marginal tax rate computation matching TAXSIM-35 methodology, enabling comparison of marginal rates between PolicyEngine and TAXSIM.

  • frate: federal income tax marginal rate
  • srate: state income tax marginal rate
  • ficar: FICA (employee payroll tax) marginal rate

Methodology (matching TAXSIM-35)

  • Perturbs employment income (wages) only — not self-employment income
  • Splits perturbation proportionally between primary and spouse earners (weighted average earnings, matching TAXSIM mtr=11)
  • Uses $100 delta (TAXSIM uses $0.01 with Fortran float64; PE's float32 requires a larger delta for precision)
  • Returns rates as percentages (22.0 for 22%) to match TAXSIM output format

Implementation

  • Microsim path (policyengine_runner.py): Uses simulation branching to compute perturbed taxes vectorized
  • Single-household path (output_mapper.py via marginal_rates.py): Deep-copies the situation and runs a second simulation
  • Both paths share the same TAXSIM-matching perturbation logic (wages-only, proportional split)

Changes

  • New policyengine_taxsim/core/marginal_rates.py — shared marginal rate computation
  • Updated variable_mappings.yaml — frate/srate/ficar marked as implemented
  • Updated policyengine_runner.py — microsim marginal rate computation via branching
  • Updated output_mapper.py — single-household marginal rate support
  • Updated dashboard constants — frate/srate/ficar now show PE variable mappings

Test plan

  • 62 existing tests pass
  • Manual verification: TX single $100k → frate=22%, srate=0%, ficar=7.65%
  • Manual verification: CA single $100k → srate=9.3%
  • Manual verification: MFJ $120k/$50k → frate=22%, proportional wage split
  • End-to-end comparison with TAXSIM-35 output (pending merge)

Dependencies

  • Blocked on policyengine-us#7791 for upstream variable support (not strictly required — this PR computes rates independently, but the upstream variables would be useful for the app)

🤖 Generated with Claude Code

@vercel
Copy link

vercel bot commented Mar 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
policyengine-taxsim Ready Ready Preview, Comment Mar 19, 2026 0:59am

Request Review

PavelMakarchuk and others added 6 commits March 19, 2026 08:04
…car)

Implements marginal rate calculation matching TAXSIM-35 methodology:
- Perturbs employment income (wages only, not self-employment)
- Splits perturbation proportionally between primary and spouse
  (weighted average earnings, matching TAXSIM mtr=11)
- Uses $100 delta for float32 precision stability
- Returns rates as percentages to match TAXSIM output format

Rates are computed via a second simulation (branch in microsim path,
deepcopy in single-household path) measuring the change in each tax
component independently:
- frate: federal income tax marginal rate
- srate: state income tax marginal rate
- ficar: FICA (employee payroll tax) marginal rate

Depends on policyengine-us#7791 for upstream variable support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add marginal rate methodology section to documentation page
- Mark frate/srate/ficar as implemented in output variable table
- Show descriptive variable names (e.g., "income_tax (via perturbation)")

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…XSIM-35

TAXSIM-35 reports ficar as the combined employee+employer FICA rate
(~15.3%), not just the employee portion (~7.65%). Updated both the
single-household and batch paths to sum employee_payroll_tax,
employer_social_security_tax, and employer_medicare_tax.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- frate now includes additional_medicare_tax in the perturbation,
  matching TAXSIM's definition of federal marginal rate
- Disable ficar (TAXSIM always returns 0 for this field)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The exe/output_mapper code path in marginal_rates.py still had the
old bugs: frate missing additional_medicare_tax and ficar still
being computed. Align with the microsim-path fixes from e4a60b6.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@PavelMakarchuk PavelMakarchuk marked this pull request as ready for review March 19, 2026 13:58
@PavelMakarchuk PavelMakarchuk merged commit aa6ca16 into main Mar 19, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant