Skip to content

Instantly share code, notes, and snippets.

@pathikrit
Last active February 16, 2025 13:25
Show Gist options
  • Save pathikrit/6f85fec0e42f381ce96c738bfd8413b6 to your computer and use it in GitHub Desktop.
Save pathikrit/6f85fec0e42f381ce96c738bfd8413b6 to your computer and use it in GitHub Desktop.
Capital Gains Tax Calculator
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