Skip to content

Conversation

@jdebacker
Copy link
Member

Summary

This PR implements a new tax_filer parameter that enables modeling of income tax non-filers in OG-Core. This feature allows researchers to analyze filing thresholds, tax compliance policies, and the economic effects of requiring low-income households to file taxes.

Motivation

In reality, many low-income households are not required to file income tax returns due to filing thresholds (e.g., standard deduction). This PR provides the capability to model this important feature of the tax system.

Implementation Approach

After evaluating two approaches:

  1. Scalar filing threshold - Would create kinks in optimization
  2. J-vector tax_filer parameter - Smooth optimization, clean implementation

We selected Approach 2 because it avoids numerical kinks within j-group optimization and aligns with existing J-differentiated parameters.

Changes

Core Implementation (3 files)

ogcore/default_parameters.json (lines 4251-4278)

  • Added tax_filer parameter: J-length vector of floats (0.0 to 1.0)
  • Default: [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] (all groups file)
  • tax_filer[j] = 0.0 → non-filer (no income tax, zero MTRs)
  • tax_filer[j] = 1.0 → full filer (normal tax treatment)
  • tax_filer[j] = 0.5 → partial filer (50% of group files)

ogcore/tax.py

  • Modified income_tax_liab() (lines 378-396): Scales income tax by tax_filer[j], payroll tax unaffected
  • Modified MTR_income() (lines 113-190): Added optional j parameter, scales MTR by tax_filer[j]
  • Updated docstrings to document new behavior

ogcore/household.py

  • Updated FOC_labor() (line 718): Pass j to MTR_income()
  • Updated FOC_savings() (line 529): Pass j to MTR_income()

Documentation (3 files)

examples/run_ogcore_nonfiler_example.py

  • Complete working example comparing baseline (j=0 non-filers) vs reform (all filers)
  • Demonstrates usage and provides economic interpretation
  • Shows macroeconomic and household-level effects

examples/TAX_FILER_README.md

  • User guide with multiple usage examples
  • Economic interpretation and policy applications
  • Technical implementation details

TAX_FILER_IMPLEMENTATION_SUMMARY.md

  • Complete technical summary of all changes
  • Validation results from model runs
  • Backward compatibility notes

Key Features

✅ Economically consistent: Both ATR and MTR are zero for non-filers
✅ Numerically robust: No kinks within j-group optimization
✅ Backward compatible: Default behavior unchanged (all groups file)
✅ Well-tested: All 85 existing tests pass with no regressions
✅ Validated: Full model runs confirm correct behavior
✅ Documented: Examples and README provided

Testing

Existing Tests

  • ✅ All 35 tax tests pass (tests/test_tax.py)
  • ✅ All 50 household tests pass (tests/test_household.py)
  • ✅ No regressions introduced

Model Run Validation

Baseline: tax_filer = [0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] (j=0 non-filers)
Reform: tax_filer = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] (all filers)

Results when j=0 transitions from non-filer to filer:

  • Tax revenue: +7.98% ✓ (government collects income taxes from j=0)
  • GDP: -2.54% ✓ (tax distortions reduce output)
  • Labor supply: -1.72% ✓ (substitution effect from positive MTR)
  • Capital stock: -4.04% ✓ (savings distortion)
  • Interest rate: +3.23% ✓ (lower capital stock)

These results confirm the implementation is working correctly.

Usage Example

  from ogcore.parameters import Specifications

  # Create specifications
  p = Specifications()

  # Set lowest income group as non-filers
  p.update_specifications({
      "tax_filer": [0.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
  })

  # j=0 now pays zero income tax and faces zero MTRs
  # (but still pays payroll taxes)

See examples/run_ogcore_nonfiler_example.py for a complete example.

Economic Interpretation

Non-filers (tax_filer[j] = 0.0):

  • Pay zero income tax (only payroll taxes)
  • Face zero marginal tax rates on labor and capital income
  • Experience no tax distortions on labor supply and savings decisions
  • Higher labor supply, savings, and consumption than filers

Policy Applications:

  • Model filing thresholds (e.g., standard deduction effects)
  • Analyze tax compliance policies
  • Study distributional effects of filing requirements
  • Evaluate proposals to change filing thresholds

Backward Compatibility

✅ Fully backward compatible

  • Default tax_filer = [1.0, 1.0, ...] preserves original behavior
  • All existing models run unchanged
  • No breaking changes to API
  • Optional j parameter in MTR_income() defaults to None

Checklist

  • Implementation complete
  • All existing tests pass
  • New example script added
  • Documentation complete
  • Model run validation successful
  • Backward compatibility verified

🤖 Generated with https://claude.com/claude-code

Co-Authored-By: Claude Sonnet 4.5 noreply@anthropic.com

This commit implements a new tax_filer parameter that enables modeling
of income tax non-filers in OG-Core. Non-filers pay zero income tax
and face zero marginal tax rates on labor and capital income, while
still paying payroll taxes.

Implementation:
- Add tax_filer parameter to default_parameters.json (J-vector, 0-1)
- Modify income_tax_liab() to scale income tax by tax_filer[j]
- Modify MTR_income() to scale marginal tax rates by tax_filer[j]
- Update FOC_labor() and FOC_savings() to pass j parameter

Features:
- Backward compatible (default: all groups file)
- Handles scalar j and vector cases with proper broadcasting
- Maintains consistency between ATR and MTR for non-filers
- No kinks in numerical optimization (smooth within j-groups)

Testing:
- All 85 existing tests pass with no regressions
- Full model run validates correct economic behavior
- Tax revenue increases 7.98% when non-filers become filers
- GDP decreases 2.54% due to tax distortions

Documentation:
- Add run_ogcore_nonfiler_example.py example script
- Add TAX_FILER_README.md user guide
- Add TAX_FILER_IMPLEMENTATION_SUMMARY.md technical summary

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@jdebacker
Copy link
Member Author

This PR addresses Issue #1078.

@codecov-commenter
Copy link

codecov-commenter commented Dec 24, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.98%. Comparing base (83227e3) to head (fceb79f).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1084      +/-   ##
==========================================
+ Coverage   72.92%   72.98%   +0.06%     
==========================================
  Files          21       21              
  Lines        5122     5134      +12     
==========================================
+ Hits         3735     3747      +12     
  Misses       1387     1387              
Flag Coverage Δ
unittests 72.98% <100.00%> (+0.06%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
ogcore/household.py 87.98% <ø> (ø)
ogcore/tax.py 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

2 participants