Last active
February 16, 2025 13:25
-
-
Save pathikrit/6f85fec0e42f381ce96c738bfd8413b6 to your computer and use it in GitHub Desktop.
Capital Gains Tax Calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from collections import defaultdict | |
from dataclasses import dataclass | |
inf = float("inf") | |
# Married filing jointly standard deduction | |
# https://apps.irs.gov/app/vita/content/00/00_13_005.jsp?level=basic | |
standard_deduction = 30_000 | |
# NJ joint filing tax brackets | |
# https://www.nj.gov/treasury/taxation/taxtables.shtml | |
state_income_tax_brackets = [ | |
{"bracket": ( 0, 20_000), "rate": 1.400/100}, | |
{"bracket": ( 20_000, 50_000), "rate": 1.750/100}, | |
{"bracket": ( 50_000, 70_000), "rate": 2.450/100}, | |
{"bracket": ( 70_000, 80_000), "rate": 3.500/100}, | |
{"bracket": ( 80_000, 150_000), "rate": 5.525/100}, | |
{"bracket": ( 150_000, 500_000), "rate": 6.370/100}, | |
{"bracket": ( 500_000, 1_000_000), "rate": 8.970/100}, | |
{"bracket": ( 1_000_000, inf), "rate": 10.750/100}, | |
] | |
# Federal income tax brackets for married filing jointly | |
# https://www.irs.gov/filing/federal-income-tax-rates-and-brackets | |
federal_income_tax_brackets = [ | |
{"bracket": ( 0, 23_850), "rate": 10/100}, | |
{"bracket": ( 23_850, 96_950), "rate": 12/100}, | |
{"bracket": ( 96_950, 206_700), "rate": 22/100}, | |
{"bracket": ( 206_700, 394_600), "rate": 24/100}, | |
{"bracket": ( 394_600, 501_050), "rate": 32/100}, | |
{"bracket": ( 501_050, 751_600), "rate": 35/100}, | |
{"bracket": ( 751_600, inf), "rate": 37/100}, | |
] | |
# Federal LTCG tax brackets for married filing jointly | |
# https://www.irs.gov/taxtopics/tc409 | |
def federal_ltcg_brackets(ordinary_income: int): | |
brackets = [ | |
{"bracket": ( 0, 96_700), "rate": 0/100}, | |
{"bracket": ( 96_700, 600_050), "rate": 15/100}, | |
{"bracket": ( 600_050, inf), "rate": 20/100}, | |
] | |
# Preferential income is "stacked" on top of ordinary income | |
# https://www.kitces.com/blog/long-term-capital-gains-bump-zone-higher-marginal-tax-rate-phase-in-0-rate/ | |
for bracket in brackets: | |
bracket["bracket"] = tuple(map(lambda x: max(0, x - ordinary_income), bracket["bracket"])) | |
return brackets | |
# Net Investment Income Tax brackets for married filing jointly | |
# https://www.irs.gov/taxtopics/tc559 | |
niit_brackets = [ | |
{"bracket": (250_000, inf), "rate": 3.8/100}, | |
] | |
@dataclass | |
class Tax: | |
total_income: float | |
federal_tax: float | |
federal_rate: float | |
state_tax: float | |
state_rate: float | |
def __init__(self, ltcg: int, stcg: int, dividends: int, qualified_dividends: int = 0): | |
stcg += dividends | |
ltcg += qualified_dividends | |
self.total_income = ltcg + stcg | |
# NJ treats any capital gain as regular income | |
self.state_tax = Tax.for_bracket(income=self.total_income, brackets=state_income_tax_brackets) | |
self.state_rate = 100*self.state_tax/self.total_income | |
stcg -= standard_deduction | |
if stcg < 0: | |
ltcg = max(0, ltcg + stcg) # apply the extra standard deduction to LTCG | |
stcg = 0 | |
self.federal_tax = sum([ | |
Tax.for_bracket(income=stcg, brackets=federal_income_tax_brackets), | |
Tax.for_bracket(income=ltcg, brackets=federal_ltcg_brackets(stcg)), | |
Tax.for_bracket(income=stcg+ltcg, brackets=niit_brackets), | |
]) | |
self.federal_rate = 100*self.federal_tax/self.total_income | |
@staticmethod | |
def for_bracket(income: float, brackets: list) -> float: | |
tax = 0 | |
for (bottom, top), rate in ((b["bracket"], b["rate"]) for b in brackets): | |
tax += max(0, min(top, income) - bottom) * rate | |
return tax | |
def __str__(self): | |
return "\n".join([ | |
f"Total Income: ${round(self.total_income)}", | |
f"Federal Taxes: ${round(self.federal_tax)}", | |
f"Federal Rate: {round(self.federal_rate, 1)}%", | |
f"State Taxes: ${round(self.state_tax)}", | |
f"State Rate: {round(self.state_rate, 1)}%", | |
f"Total Taxes: ${round(self.federal_tax + self.state_tax)}", | |
f"Total Rate: {round(self.federal_rate + self.state_rate, 1)}%", | |
f"Income after taxes: ${round(self.total_income - self.federal_tax - self.state_tax)}" | |
]) | |
if __name__ == "main": | |
print(Tax(ltcg=500_000, stcg=0, dividends=250_000)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment