Metadata-Version: 2.4
Name: tenforty
Version: 2025.12
Summary: Library to compute US federal taxes, and state taxes for some states.
Author-email: Mike Macpherson <mmacpherson@users.noreply.github.com>
License: MIT License
        
        Copyright (c) 2020 Mike Macpherson
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Classifier: Programming Language :: Cython
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: License :: OSI Approved :: MIT License
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Topic :: Office/Business :: Financial :: Accounting
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: polars>=1.0
Requires-Dist: pydantic>=2
Requires-Dist: python-dotenv
Provides-Extra: dev
Requires-Dist: hypothesis; extra == "dev"
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: pytest-xdist>=3.2; extra == "dev"
Requires-Dist: ruff>=0.2.0; extra == "dev"
Requires-Dist: pre-commit; extra == "dev"
Provides-Extra: jupyter
Requires-Dist: jupyter; extra == "jupyter"
Requires-Dist: seaborn; extra == "jupyter"
Requires-Dist: pyarrow; extra == "jupyter"
Requires-Dist: ipykernel; extra == "jupyter"
Dynamic: license-file

# tenforty

[![GitHub Actions](https://github.com/mmacpherson/tenforty/actions/workflows/ci.yml/badge.svg)](https://github.com/mmacpherson/tenforty/actions/workflows/ci.yml)
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/mmacpherson/tenforty/main.svg)](https://results.pre-commit.ci/latest/github/mmacpherson/tenforty/main)

[![PyPI](https://img.shields.io/pypi/v/tenforty.svg)](https://pypi.org/project/tenforty/)
[![Python Version](https://img.shields.io/pypi/pyversions/tenforty.svg)](https://pypi.org/project/tenforty/)
[![Downloads](https://img.shields.io/pypi/dm/tenforty.svg)](https://pypi.org/project/tenforty/)

[![License](https://img.shields.io/github/license/mmacpherson/tenforty.svg)](https://github.com/mmacpherson/tenforty/blob/main/LICENSE.txt)
[![Operating System](https://img.shields.io/badge/OS-Linux_%7C_macOS-blue)](https://pypi.org/project/tenforty/)
[![Built with uv](https://img.shields.io/badge/Built%20with-uv-purple.svg)](https://github.com/astral-sh/uv)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)


## Overview

`tenforty` is an open-source Python package designed to help demystify US
federal and state tax computations. This project offers an accessible way to
explore tax scenarios, compare different tax situations, and understand the
impact of various factors on tax liabilities. It's particularly useful for those
who would like to understand or optimize their taxes by evaluating how tax form
inputs affect their outputs.

The package is built on top of the
[Open Tax Solver](https://opentaxsolver.sourceforge.net/)
project, wrapping its
functionality into a Python library.

The package and the ideas behind it are discussed in a blog post
[here](https://finedataproducts.com/posts/2024-03-10-tax-scenarios-with-ai/).
`tenforty` also works well driven by a coding agent (Claude Code, Codex, and
friends) — the validated inputs and informative error messages make it a good
tool-call target.

You can try `tenforty` out immediately in your browser via the included Colab
notebook: [![Try In
Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/mmacpherson/tenforty/blob/main/notebooks/tenforty_Package_Demo.ipynb)


## Features

- Compute US federal taxes, as well as taxes for several US states.
- Explore how taxes vary as a function of income, state, filing status, and year.
- Easily integrate with data analysis and visualization tools in Python with `polars` support.
- Evaluate "what if" tax scenarios efficiently and reproducibly.


## Related Projects

If you've landed here while looking for open-source US tax computation, it's
worth knowing the landscape — a few excellent projects approach this problem
from different angles, and one of them may fit your task better:

- **[Tax-Calculator](https://github.com/PSLmodels/Tax-Calculator)** (Policy
  Simulation Library) is a federal income and payroll tax microsimulation
  model with a long pedigree in the policy-economics world. Its special power
  is *counterfactual law*: every rate, bracket, and phase-out is a reformable
  parameter, so it can answer "what would this filer (or the whole country)
  pay under a proposed reform?" It models per-spouse income attribution and
  has been validated against NBER's TAXSIM for years. If your question is
  about policy analysis, revenue estimation, or distributional effects, start
  there. `tenforty` uses Tax-Calculator as an independent validation oracle
  for its own federal results — see
  [docs/taxcalc-differential-audit.md](./docs/taxcalc-differential-audit.md).
- **[TAXSIM](https://taxsim.nber.org/)** (NBER) is the long-standing academic
  reference model for US federal and state income taxes, callable remotely.
- **[PolicyEngine-US](https://github.com/PolicyEngine/policyengine-us)**
  models federal and many state taxes alongside benefit programs, with a
  policy-reform focus similar to Tax-Calculator's.

What `tenforty` brings to this neighborhood: state returns computed locally,
form-level fidelity (actual form lines, via Open Tax Solver), exact marginal
rates via automatic differentiation, an input solver, and a deliberately
simple API. It's built for an individual interrogating *their own return* —
sensitivity analysis, what-if sweeps, optimization. Performance depends on
the backend, graph, and API path: a small Rust demo-graph benchmark is not a
measurement of a complete return through Python or the browser. The shipped
Python and browser graph paths use the interpreter, not the experimental JIT.
If your
question is "what would *this return* look like, federal and state, and how
does it change as inputs move?" — that's the job this package is built for.


## Disclaimer

`tenforty` is an open-source tool intended for informational and educational
purposes only and does not provide tax advice.

Known limitations of this package are detailed in the
[Limitations](#known-limitations) section below.

## Installation

Python 3.10–3.14 is supported on Linux and macOS. Python 3.15 is experimental:
CI builds and installs a wheel on both platforms, requires both the OTS and
Rust graph backends, and runs the standard test suite. These checks cover the
regular CPython build, not the free-threaded build or optional notebook and
Tax-Calculator dependencies.

Use Python 3.10–3.14 for supported installations. Prebuilt Python 3.15 wheels
are not published yet; testing 3.15 from source requires a C++ compiler and Rust,
and dependencies may also need to compile from source. Full 3.15 support will
be reconsidered after the final Python release and wheel validation.

```sh
pip install tenforty
```


## Main Functions Documentation

The two functions `evaluate_return` and `evaluate_returns` are the main
interface to `tenforty`. They take exactly the same arguments, except that any
of the arguments to `evaluate_returns` may either be a single value, or a list
of values. `evaluate_return` is for evaluating one single return, and
`evaluate_returns` evaluates all combinations of inputs subtended by the
provided values and collects the results into a dataframe.

The inputs to either function are validated, and if for example a filing status
is misspelled, you'll get an informative error message along with a list of the
valid options.

Here are all arguments available for those two functions:


| Argument                     | Type                     | Default             | Notes                              |
|------------------------------|--------------------------|---------------------|------------------------------------|
| `year`                       | int                      | 2025                | 2018-2025 inclusive                |
| `state`                      | str \| None               | None                | Two-letter state code. Income-tax states with OTS support: AZ, CA, MA, MI, NC, NJ, NY, OH, OR, PA, VA. No-income-tax states (AK, FL, NV, SD, TN, TX, WA, WY) also accepted. Other states unsupported for now. |
| `filing_status`              | str                      | Single              | "Single", "Married/Joint", "Head_of_House", "Married/Sep", "Widow(er)" |
| `num_dependents`             | int                      | 0                   |                                    |
| `standard_or_itemized`       | str                      | Standard            | Legacy choice: "Standard" is automatic greater-of; "Itemized" forces federal itemization ([contract](docs/deduction-choice-contract.md)) |
| `w2_income`                  | float                    | 0.0                 |                                    |
| `taxable_interest`           | float                    | 0.0                 |                                    |
| `qualified_dividends`        | float                    | 0.0                 |                                    |
| `ordinary_dividends`         | float                    | 0.0                 |                                    |
| `short_term_capital_gains`   | float                    | 0.0                 |                                    |
| `long_term_capital_gains`    | float                    | 0.0                 |                                    |
| `self_employment_income`     | float                    | 0.0                 | Schedule C profit used by Schedule SE and QBI |
| `qbi_w2_wages`               | float                    | 0.0                 | W-2 wages paid by the QBI business or aggregation |
| `qbi_ubia`                   | float                    | 0.0                 | UBIA of qualified property for that business or aggregation |
| `qbi_is_sstb`                | bool                     | False               | Whether that business or aggregation is an SSTB |
| `schedule_1_income`          | float                    | 0.0                 |                                    |
| `itemized_deductions`        | float                    | 0.0                 |                                    |
| `state_adjustment`           | float                    | 0.0                 |                                    |
| `incentive_stock_option_gains` | float                  | 0.0                 |                                    |

The three `qbi_` inputs describe one trade or business, or businesses the
taxpayer has validly aggregated for section 199A. They are not totals across
unrelated businesses, because Form 8995-A applies its wage and UBIA limitation
per business or valid aggregation. Their defaults model a non-SSTB business
with no business W-2 wages or qualified property. The OTS backend uses OTS's
Form 8995 for the independent QBI base and taxable-income ceiling, then applies
the missing Form 8995-A wage, property, and SSTB limitation in tenforty's
orchestration layer before rerunning Form 1040.

The functions output these fields:

| Output Field                    | Description                                           |
|---------------------------------|-------------------------------------------------------|
| total_tax                       | Combined federal and state tax liability              |
| federal_adjusted_gross_income   | Federal Adjusted Gross Income (Form 1040 Line 11)     |
| federal_effective_tax_rate      | Percentage of AGI paid in federal tax                 |
| federal_tax_bracket             | Marginal federal tax bracket (0-37%)                  |
| federal_taxable_income          | Income subject to federal tax after deductions        |
| federal_qbi_deduction           | Qualified business income deduction (Form 8995/8995-A) |
| federal_amt                     | Federal Alternative Minimum Tax                       |
| federal_income_tax              | Federal income tax + AMT (excludes SE tax, NIIT, Additional Medicare Tax) |
| federal_se_tax                  | Federal self-employment tax (Schedule SE)             |
| federal_niit                    | Net Investment Income Tax (Form 8960)                 |
| federal_additional_medicare_tax | Additional Medicare Tax (Form 8959)                   |
| federal_total_tax               | Total federal tax (income tax + SE + NIIT + Additional Medicare) |
| state_adjusted_gross_income     | State-level Adjusted Gross Income                     |
| state_taxable_income            | Income subject to state tax after deductions          |
| state_total_tax                 | Total state tax liability                             |
| state_tax_bracket               | Marginal state tax bracket                            |
| state_effective_tax_rate        | Percentage of state AGI paid in state tax             |



## Examples

Here are some examples of what you can do with `tenforty`:

### Basic Evaluation

The `evaluate_return` function computes the outputs for a single tax return
given some inputs:

``` python
from tenforty import evaluate_return

evaluate_return(
    w2_income=100_000, state="CA", filing_status="Married/Joint", num_dependents=2
).model_dump()
```

This results in the following:

``` python
{'total_tax': 8484.0,
 'federal_adjusted_gross_income': 100000.0,
 'federal_effective_tax_rate': 11.4,
 'federal_tax_bracket': 12.0,
 'federal_taxable_income': 74100.0,
 'federal_amt': 0.0,
 'federal_income_tax': 8484.0,
 'federal_se_tax': 0.0,
 'federal_niit': 0.0,
 'federal_additional_medicare_tax': 0.0,
 'federal_total_tax': 8484.0,
 'state_adjusted_gross_income': 0.0,
 'state_taxable_income': 0.0,
 'state_total_tax': 0.0,
 'state_tax_bracket': 0.0,
 'state_effective_tax_rate': 0.0}
```

No `year=` argument was specified here, so the current tax year, 2025, was used.
The output is a pydantic model, and we've called its `.model_dump()` method to
show the result as a dictionary.


### Marginal Rates for Every Input

The graph backend can differentiate a selected output with respect to every
continuous public input at once:

``` python
from tenforty import marginal_rates

rates = marginal_rates(
    year=2024,
    state="CA",
    filing_status="Single",
    w2_income=100_000,
)

rates["w2_income"]
```

The returned dictionary uses the same natural input names as
`evaluate_return`. It includes federal inputs and any continuous inputs mapped
by the selected state; discrete choices such as filing status and number of
dependents are omitted. The existing `marginal_rate(..., wrt="w2_income")`
function remains available for one input at a time.

Tax calculations are piecewise functions. At a bracket, phase-out, or other
active boundary, both marginal-rate APIs report the composed function's
right-hand derivative: the change implied by adding a small amount to that
input. Smooth entries in `marginal_rates` share one reverse-mode traversal per
resolved output.

The reproducible
[autodiff planning examples](docs/autodiff-applications.md) show a full
next-dollar table, the interaction between wages and long-term-gain stacking,
and a bounded ordinary-income sizing exercise. They also measure where forward
simulation remains competitive and explain why derivatives alone cannot find
true tax cliffs.

### Creating Tax Tables: Federal/State Tax Brackets as a Function of W2 Income

The `evaluate_returns` method sweeps out a grid over any input arguments that
are provided as lists, allowing you to evaluate a wide array of tax scenarios.
Here we make a simple tax table by varying W2 income:

``` python
from tenforty import evaluate_returns

evaluate_returns(
    w2_income=list(range(50_000, 250_001, 50_000)),
    state="CA",
    filing_status="Married/Joint",
    num_dependents=2,
)[
    [
        "w2_income",
        "federal_effective_tax_rate",
        "federal_tax_bracket",
        "state_effective_tax_rate",
        "state_tax_bracket",
    ]
]
```

This results in a `polars.DataFrame` (use `.to_pandas()` if you need pandas compatibility):

|   w2_income |   federal_effective_tax_rate |   federal_tax_bracket |   state_effective_tax_rate |   state_tax_bracket |
|------------:|-----------------------------:|----------------------:|---------------------------:|--------------------:|
|       50000 |                         10.3 |                    12 |                        1.5 |                 2   |
|      100000 |                         11.4 |                    12 |                        3   |                 6   |
|      150000 |                         14.9 |                    22 |                        4.6 |                 9.3 |
|      200000 |                         17   |                    22 |                        5.9 |                 9.3 |
|      250000 |                         18.5 |                    24 |                        6.6 |                 9.3 |


### Plot: Federal Tax as a Function of W2 Income

Since the output is a dataframe, one may readily use any of numerous
visualization tools to make plots. Here we revisit the example above, evaluating
a wider range of W2 incomes at finer resolution than before.

``` python
import seaborn.objects as so

df = evaluate_returns(w2_income=list(range(0, 250_001, 1_000)))

(
    so.Plot(df, x="w2_income", y="total_tax")
    .add(so.Line())
    .label(
        x="W2 Income", y="Federal Tax", title="Federal Tax as a Function of W2 Income"
    )
)
```

![Image: Federal Tax as a Function of W2 Income](images/example1.svg)


### Plot: Federal Tax Over Time

The good people at Open Tax Solver have published editions each year for 21
years, so one can just as easily vary the year as any other parameter. At the
moment `tenforty` supports back to the 2018 tax year. Here we show the federal
tax on $100K of W2 income for the past five years.

``` python
import polars as pl

df = evaluate_returns(
    year=[2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025], w2_income=100_000
).cast({"year": pl.Utf8})

(
    so.Plot(df, x="year", y="total_tax")
    .add(so.Line())
    .add(so.Dot())
    .label(
        x="Year",
        y="Federal Tax",
        title="Federal Tax on $100K W2 Income Over Time",
    )
)
```

![Image: Federal Tax Over Time](images/example2.svg)

*This one's a little melodramatic because we don't make the y-axis go to zero;
it's only about a 3% drop over the years.*


### Plot: Impact of Long-Term Capital Gains

Because Open Tax Solver supports short- and long-term capitals gains
calculations -- although, see the [Limitations](#known-limitations) section
below -- you can ask questions about the impact on your taxes of selling some
appreciated stock this year, and show the breakdown between state and federal
taxes:

``` python
import polars as pl

df = (
    evaluate_returns(
        w2_income=75_000,
        state="CA",
        long_term_capital_gains=list(range(0, 125_001, 5000)),
    )
    .select(["long_term_capital_gains", "state_total_tax", "federal_total_tax"])
    .unpivot(index="long_term_capital_gains", variable_name="Type", value_name="tax")
    .with_columns(
        pl.col("Type").replace(
            {"state_total_tax": "State", "federal_total_tax": "Federal"}
        )
    )
)

(
    so.Plot(df, x="long_term_capital_gains", y="tax", color="Type").add(
        so.Area(alpha=0.7), so.Stack()
    )
    .label(
        x="Long-Term Capital Gains",
        y="Total Tax",
        title="Impact of LTCG on Total Tax for California Resident",
    )
)
```

![Image: Impact of Long-Term Capital Gains](images/example3.svg)


### Plot: Will I Incur Alternative Minimum Tax (AMT)?

Employees at tech companies are commonly issued incentive stock options, the
exercise of which can put them in a situation where they need to pay actual
money in taxes on paper gains, via the alternative minimum tax. With
`tenforty`'s help you can see it coming at least: ;)


``` python
import polars as pl

df = (
    tenforty.evaluate_returns(
        w2_income=100_000, incentive_stock_option_gains=list(range(0, 100_001, 2500))
    )
    .select(["incentive_stock_option_gains", "federal_total_tax", "federal_amt"])
    .unpivot(index="incentive_stock_option_gains", variable_name="Type", value_name="tax")
    .with_columns(
        pl.col("Type").replace(
            {"federal_amt": "AMT", "federal_total_tax": '"Regular" Tax'}
        )
    )
)

(
    so.Plot(df, x="incentive_stock_option_gains", y="tax", color="Type")
    .add(so.Area(alpha=0.7), so.Stack())
    .label(
        x="Incentive Stock Option Gains",
        y="Total Federal Tax",
        title="Effect of ISO Gains on Federal Alternative Minimum Tax\nGiven $100K W2 Income",
    )
)
```

![Image: Am I in AMT?](images/example4.svg)



## Known Limitations

- Currently does not support Windows. The Colab notebook linked above, or using
  WSL are workarounds. Attempts have been made to get Windows builds working, but
  runtime crashes persist due to compiler interoperability challenges; see
  [Windows Build Research](./docs/windows-build-research.md) for details.
- Medicare and Net Investment Income Tax are not automatically computed on
  capital gains, so if those apply to your situation the output tax will be
  underestimated.
- State income tax is supported for a growing number of states, with more being
  added. No-income-tax states (e.g. Texas, Nevada) are also accepted. Only
  California has been tested against tax returns prepared independently by
  professional tax software; other states have formula-derived and property-based
  testing. See [IRS Validation](./docs/irs-validation.md) for details.


## Development & Contributing

Contributions are welcome! See our [Contributing Guidelines](CONTRIBUTING.md) for the development process and coding standards.

Documentation for developers:
- [Development Guide](./docs/develop.md) - Package internals, Cython extensions, and environment setup
- [Error Handling](./docs/error-handling.md) - Configuring `on_error` policies and exception types
- [IRS Validation](./docs/irs-validation.md) - Testing methodology against official IRS and state sources


## License

`tenforty` is released under the MIT License.


## Acknowledgments

This project relies on the [Open Tax
Solver](https://opentaxsolver.sourceforge.net/) project for the underlying tax
computation logic.
